From 85ce2c8d18db26ef01547dcfc17aec2766f1bb21 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 7 Sep 2020 10:10:23 -0700 Subject: [PATCH 01/41] Migrate from go-ps to gopsutil for better process detection (#597) * Migrate from go-ps to gopsutil for better process detection * Remove debugging code --- docs/manual.md | 2 -- go.mod | 5 ++++- go.sum | 10 ++++++++-- system/process.go | 16 +++++++++------- system/system.go | 7 ++++--- 5 files changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/manual.md b/docs/manual.md index cc561e36e..2121ff1f6 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -772,8 +772,6 @@ process: skip: false ``` -**NOTE:** This check is inspecting the name of the binary, not the name of the process. For example, a process with the name `nginx: master process /usr/sbin/nginx` would be checked with the process `nginx`. To discover the binary of a pid run `ps -p -o comm`. - ### service Validates the state of a service. diff --git a/go.mod b/go.mod index bd686efc2..cfcb64759 100644 --- a/go.mod +++ b/go.mod @@ -4,13 +4,14 @@ require ( github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect 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/blang/semver v3.5.1+incompatible github.com/cheekybits/genny v1.0.0 github.com/docker/docker v1.13.1 github.com/fatih/color v1.9.0 + github.com/go-ole/go-ole v1.2.4 // indirect github.com/google/uuid v1.1.1 // indirect github.com/huandu/xstrings v1.3.0 // indirect github.com/imdario/mergo v0.3.8 // indirect @@ -20,8 +21,10 @@ require ( github.com/onsi/gomega v1.9.0 github.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/shirou/gopsutil v2.20.6+incompatible github.com/stretchr/testify v1.4.0 github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 + golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect gopkg.in/yaml.v2 v2.2.8 ) diff --git a/go.sum b/go.sum index 026e3ea2b..c5dcf93ca 100644 --- a/go.sum +++ b/go.sum @@ -4,12 +4,12 @@ github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3Q github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2 h1:NYoPVh1XuUB5VBWLXRKoqzQhl4bajIxh+XuURbJ0uwc= github.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2/go.mod h1:DCNKSpXhum14Y258jSbRmJvcesbzEdBPincz7yJUx3k= github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 h1:oD15ssIOuFLi64zhkPRsaIDvhx4PeZb2QdQoR/wKY2g= 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/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= @@ -22,6 +22,8 @@ github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= @@ -55,6 +57,8 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.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/shirou/gopsutil v2.20.6+incompatible h1:P37G9YH8M4vqkKcwBosp+URN5O8Tay67D2MbR361ioY= +github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -79,6 +83,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= diff --git a/system/process.go b/system/process.go index a88a97e36..8edddbdde 100644 --- a/system/process.go +++ b/system/process.go @@ -1,8 +1,8 @@ package system import ( - "github.com/aelsabbahy/go-ps" "github.com/aelsabbahy/goss/util" + "github.com/shirou/gopsutil/process" ) type Process interface { @@ -14,7 +14,7 @@ type Process interface { type DefProcess struct { executable string - procMap map[string][]ps.Process + procMap map[string][]*process.Process err error } @@ -39,7 +39,7 @@ func (p *DefProcess) Pids() ([]int, error) { return pids, p.err } for _, proc := range p.procMap[p.executable] { - pids = append(pids, proc.Pid()) + pids = append(pids, int(proc.Pid)) } return pids, nil } @@ -54,14 +54,16 @@ func (p *DefProcess) Running() (bool, error) { return false, nil } -func GetProcs() (map[string][]ps.Process, error) { - pmap := make(map[string][]ps.Process) - processes, err := ps.Processes() +func GetProcs() (map[string][]*process.Process, error) { + pmap := make(map[string][]*process.Process) + processes, err := process.Processes() if err != nil { return pmap, err } for _, p := range processes { - pmap[p.Executable()] = append(pmap[p.Executable()], p) + if pExe, err := p.Name(); err == nil { + pmap[pExe] = append(pmap[pExe], p) + } } return pmap, nil diff --git a/system/system.go b/system/system.go index 9573367e0..161850c0d 100644 --- a/system/system.go +++ b/system/system.go @@ -9,8 +9,9 @@ import ( "sync" "github.com/aelsabbahy/GOnetstat" + "github.com/shirou/gopsutil/process" + // This needs a better name - "github.com/aelsabbahy/go-ps" util2 "github.com/aelsabbahy/goss/util" ) @@ -37,7 +38,7 @@ type System struct { NewHTTP func(string, *System, util2.Config) HTTP ports map[string][]GOnetstat.Process portsOnce sync.Once - procMap map[string][]ps.Process + procMap map[string][]*process.Process procOnce sync.Once } @@ -48,7 +49,7 @@ func (s *System) Ports() map[string][]GOnetstat.Process { return s.ports } -func (s *System) ProcMap() (map[string][]ps.Process, error) { +func (s *System) ProcMap() (map[string][]*process.Process, error) { var err error s.procOnce.Do(func() { From 6c123f8d270363bc2e33b6b53d71b63d2cc1c76e Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Fri, 13 Nov 2020 06:22:45 -0800 Subject: [PATCH 02/41] Mount (#601) * Add stale.yml config * Change stale.yml label * Add VfsOpts to mount * Use mountinfo filter --- .github/stale.yml | 16 ++++++++++++++++ go.mod | 3 ++- go.sum | 6 ++++-- system/mount.go | 22 ++++++++++------------ 4 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..331880c52 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,16 @@ +# Number of days of inactivity before an issue becomes stale +daysUntilStale: 60 +# Number of days of inactivity before a stale issue is closed +daysUntilClose: 7 +# Issues with these labels will never be considered stale +exemptLabels: + - approved +# Label to use when marking an issue as stale +staleLabel: stale +# Comment to post when marking an issue as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. +# Comment to post when closing a stale issue. Set to `false` to disable +closeComment: false diff --git a/go.mod b/go.mod index cfcb64759..5613f101b 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 github.com/blang/semver v3.5.1+incompatible github.com/cheekybits/genny v1.0.0 - github.com/docker/docker v1.13.1 github.com/fatih/color v1.9.0 github.com/go-ole/go-ole v1.2.4 // indirect github.com/google/uuid v1.1.1 // indirect @@ -17,12 +16,14 @@ require ( github.com/imdario/mergo v0.3.8 // indirect github.com/miekg/dns v1.1.27 github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/moby/sys/mountinfo v0.1.3 github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f github.com/onsi/gomega v1.9.0 github.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a github.com/patrickmn/go-cache v2.1.0+incompatible github.com/shirou/gopsutil v2.20.6+incompatible github.com/stretchr/testify v1.4.0 + github.com/thoas/go-funk v0.7.0 github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect gopkg.in/yaml.v2 v2.2.8 diff --git a/go.sum b/go.sum index c5dcf93ca..8debd912d 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,6 @@ github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitf github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= -github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -45,6 +43,8 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/sys/mountinfo v0.1.3 h1:KIrhRO14+AkwKvG/g2yIpNMOUVZ02xNhOw8KY1WsLOI= +github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f h1:I6mXuorHlvwNDFelz7a+j0HaGYSzX7+Gq60DqLVypfc= github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w= github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= @@ -62,6 +62,8 @@ github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y= +github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 h1:niRuEF0NOlFnqraxzjuvvOdCM6gxmHiaBABjvg3/kDo= github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/system/mount.go b/system/mount.go index b2cfd24fe..db5e85ead 100644 --- a/system/mount.go +++ b/system/mount.go @@ -5,7 +5,8 @@ import ( "strings" "github.com/aelsabbahy/goss/util" - "github.com/docker/docker/pkg/mount" + "github.com/moby/sys/mountinfo" + "github.com/thoas/go-funk" ) type Mount interface { @@ -21,7 +22,7 @@ type DefMount struct { mountPoint string loaded bool exists bool - mountInfo *mount.Info + mountInfo *mountinfo.Info usage int err error } @@ -77,8 +78,9 @@ func (m *DefMount) Opts() ([]string, error) { if err := m.setup(); err != nil { return nil, err } + allOpts := strings.Split(strings.Join([]string{m.mountInfo.Opts, m.mountInfo.VfsOpts}, ","), ",") - return strings.Split(m.mountInfo.Opts, ","), nil + return funk.UniqString(allOpts), nil } func (m *DefMount) Source() (string, error) { @@ -105,17 +107,13 @@ func (m *DefMount) Usage() (int, error) { return m.usage, nil } -func getMount(mountpoint string) (*mount.Info, error) { - entries, err := mount.GetMounts() +func getMount(mountpoint string) (*mountinfo.Info, error) { + entries, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(mountpoint)) if err != nil { return nil, err } - - // Search the table for the mountpoint - for _, e := range entries { - if e.Mountpoint == mountpoint { - return e, nil - } + if len(entries) == 0 { + return nil, fmt.Errorf("Mountpoint not found") } - return nil, fmt.Errorf("Mountpoint not found") + return entries[0], nil } From 3f8f5865466d207406549645376ea29f3405fb13 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 24 Dec 2020 07:55:12 -0800 Subject: [PATCH 03/41] Fix build failures --- go.mod | 2 +- go.sum | 5 ++- .../goss/centos7/goss-expected.yaml | 3 ++ integration-tests/goss/centos7/goss.yaml | 37 ++++++++++++------- .../goss/trusty/goss-expected.yaml | 3 ++ .../goss/wheezy/goss-expected.yaml | 3 ++ integration-tests/test.sh | 11 +----- system/mount.go | 4 +- 8 files changed, 40 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 1eabae93a..4bfaf5a6b 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/imdario/mergo v0.3.8 // indirect github.com/miekg/dns v1.1.35 github.com/mitchellh/copystructure v1.0.0 // indirect - github.com/moby/sys/mountinfo v0.1.3 + github.com/moby/sys/mountinfo v0.4.0 github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f github.com/onsi/gomega v1.10.4 github.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a diff --git a/go.sum b/go.sum index fe19dc038..e73739a68 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMK github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/sys/mountinfo v0.1.3 h1:KIrhRO14+AkwKvG/g2yIpNMOUVZ02xNhOw8KY1WsLOI= -github.com/moby/sys/mountinfo v0.1.3/go.mod h1:w2t2Avltqx8vE7gX5l+QiBKxODu2TX0+Syr3h52Tw4o= +github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f h1:I6mXuorHlvwNDFelz7a+j0HaGYSzX7+Gq60DqLVypfc= @@ -101,6 +101,7 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/integration-tests/goss/centos7/goss-expected.yaml b/integration-tests/goss/centos7/goss-expected.yaml index 1d82b5da3..8ff05f989 100644 --- a/integration-tests/goss/centos7/goss-expected.yaml +++ b/integration-tests/goss/centos7/goss-expected.yaml @@ -151,6 +151,9 @@ mount: opts: - rw - nosuid + - seclabel + - size=65536k + - mode=755 source: tmpfs filesystem: tmpfs http: diff --git a/integration-tests/goss/centos7/goss.yaml b/integration-tests/goss/centos7/goss.yaml index bca8f8bd7..3710dd360 100644 --- a/integration-tests/goss/centos7/goss.yaml +++ b/integration-tests/goss/centos7/goss.yaml @@ -1,4 +1,13 @@ ---- +addr: + tcp://127.0.0.1:80: + local-address: 127.0.0.1 + reachable: true + timeout: 500 +port: + tcp:80: + listening: true + ip: + - 0.0.0.0 service: autofs: enabled: false @@ -10,7 +19,7 @@ user: gid: 48 groups: - apache - home: "/usr/share/httpd" + home: /usr/share/httpd group: apache: exists: true @@ -18,15 +27,17 @@ group: process: httpd: running: true -port: - tcp:80: - listening: true - ip: - - '0.0.0.0' -addr: - tcp://127.0.0.1:80: - reachable: true - timeout: 500 - local-address: 127.0.0.1 gossfile: - "../goss-s*.yaml": {} + ../goss-s*.yaml: {} +mount: + /dev: + exists: true + opts: + - rw + - nosuid + - context="system_u:object_r:container_file_t:s0:c321 + - c460" + - size=65536k + - mode=755 + source: tmpfs + filesystem: tmpfs diff --git a/integration-tests/goss/trusty/goss-expected.yaml b/integration-tests/goss/trusty/goss-expected.yaml index 9aad00c7d..079c04645 100644 --- a/integration-tests/goss/trusty/goss-expected.yaml +++ b/integration-tests/goss/trusty/goss-expected.yaml @@ -151,6 +151,9 @@ mount: opts: - rw - nosuid + - seclabel + - size=65536k + - mode=755 source: tmpfs filesystem: tmpfs http: diff --git a/integration-tests/goss/wheezy/goss-expected.yaml b/integration-tests/goss/wheezy/goss-expected.yaml index d500dff16..dffa73308 100644 --- a/integration-tests/goss/wheezy/goss-expected.yaml +++ b/integration-tests/goss/wheezy/goss-expected.yaml @@ -151,6 +151,9 @@ mount: opts: - rw - nosuid + - seclabel + - size=65536k + - mode=755 source: tmpfs filesystem: tmpfs http: diff --git a/integration-tests/test.sh b/integration-tests/test.sh index d3907923a..59f8f1ac7 100755 --- a/integration-tests/test.sh +++ b/integration-tests/test.sh @@ -9,15 +9,6 @@ arch="${2:?"Need arch as 2nd arg. e.g. amd64 386"}" vars_inline="{inline: bar, overwrite: bar}" -seccomp_opts() { - local docker_ver minor_ver - docker_ver=$(docker version -f '{{.Client.Version}}') - minor_ver=$(cut -d'.' -f2 <<<$docker_ver) - if ((minor_ver>=10)); then - echo '--security-opt seccomp:unconfined' - fi -} - # setup places us inside repo-root; this preserves current behaviour with least change. cd integration-tests @@ -39,7 +30,7 @@ docker_exec() { if docker ps -a | grep "$container_name";then docker rm -vf "$container_name" fi -opts=(--env OS=$os --cap-add SYS_ADMIN -v "$PWD/goss:/goss" -d --name "$container_name" $(seccomp_opts)) +opts=(--env OS=$os --cap-add SYS_ADMIN -v "$PWD/goss:/goss" -d --name "$container_name" --security-opt seccomp:unconfined --security-opt label:disable) id=$(docker run "${opts[@]}" "aelsabbahy/goss_$os" /sbin/init) ip=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' "$id") trap "rv=\$?; docker rm -vf $id; exit \$rv" INT TERM EXIT diff --git a/system/mount.go b/system/mount.go index db5e85ead..fb642deb5 100644 --- a/system/mount.go +++ b/system/mount.go @@ -78,7 +78,7 @@ func (m *DefMount) Opts() ([]string, error) { if err := m.setup(); err != nil { return nil, err } - allOpts := strings.Split(strings.Join([]string{m.mountInfo.Opts, m.mountInfo.VfsOpts}, ","), ",") + allOpts := strings.Split(strings.Join([]string{m.mountInfo.Options, m.mountInfo.VFSOptions}, ","), ",") return funk.UniqString(allOpts), nil } @@ -96,7 +96,7 @@ func (m *DefMount) Filesystem() (string, error) { return "", err } - return m.mountInfo.Fstype, nil + return m.mountInfo.FSType, nil } func (m *DefMount) Usage() (int, error) { From 142c5623d4c925e5109c6ae2409f7e9e6f27a0f7 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 24 Dec 2020 08:08:36 -0800 Subject: [PATCH 04/41] Normalize seliux mount options for integration testing --- integration-tests/goss/centos7/goss-expected.yaml | 1 - integration-tests/goss/generate_goss.sh | 3 +++ integration-tests/goss/trusty/goss-expected.yaml | 1 - integration-tests/goss/wheezy/goss-expected.yaml | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/integration-tests/goss/centos7/goss-expected.yaml b/integration-tests/goss/centos7/goss-expected.yaml index 8ff05f989..0ca87a811 100644 --- a/integration-tests/goss/centos7/goss-expected.yaml +++ b/integration-tests/goss/centos7/goss-expected.yaml @@ -151,7 +151,6 @@ mount: opts: - rw - nosuid - - seclabel - size=65536k - mode=755 source: tmpfs diff --git a/integration-tests/goss/generate_goss.sh b/integration-tests/goss/generate_goss.sh index 0cdfc3daa..433d03bf0 100755 --- a/integration-tests/goss/generate_goss.sh +++ b/integration-tests/goss/generate_goss.sh @@ -59,6 +59,8 @@ goss a "${args[@]}" process $package foobar goss a "${args[@]}" kernel-param kernel.ostype goss a "${args[@]}" mount /dev +# Make SELinux test consistent +sed -i '/- seclabel/d' $SCRIPT_DIR/${OS}/goss-generated-$ARCH.yaml goss a "${args[@]}" http https://www.google.com @@ -78,6 +80,7 @@ $SCRIPT_DIR/$OS/goss-linux-$ARCH -g $SCRIPT_DIR/${OS}/goss-aa-generated-$ARCH.ya $SCRIPT_DIR/$OS/goss-linux-$ARCH -g $SCRIPT_DIR/${OS}/goss-aa-generated-$ARCH.yaml aa $package # Validate that we can aa none existent resources without destroying the file $SCRIPT_DIR/$OS/goss-linux-$ARCH -g $SCRIPT_DIR/${OS}/goss-aa-generated-$ARCH.yaml aa nosuchresource + if [[ ! -f $SCRIPT_DIR/${OS}/goss-aa-generated-$ARCH.yaml ]] then echo "Error! Config file removed by aa!" && exit 1 diff --git a/integration-tests/goss/trusty/goss-expected.yaml b/integration-tests/goss/trusty/goss-expected.yaml index 079c04645..c2c6cc353 100644 --- a/integration-tests/goss/trusty/goss-expected.yaml +++ b/integration-tests/goss/trusty/goss-expected.yaml @@ -151,7 +151,6 @@ mount: opts: - rw - nosuid - - seclabel - size=65536k - mode=755 source: tmpfs diff --git a/integration-tests/goss/wheezy/goss-expected.yaml b/integration-tests/goss/wheezy/goss-expected.yaml index dffa73308..f06545736 100644 --- a/integration-tests/goss/wheezy/goss-expected.yaml +++ b/integration-tests/goss/wheezy/goss-expected.yaml @@ -151,7 +151,6 @@ mount: opts: - rw - nosuid - - seclabel - size=65536k - mode=755 source: tmpfs From e787e01bf78d54465886d23b727dcf6ce1b7127c Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 24 Dec 2020 08:21:21 -0800 Subject: [PATCH 05/41] Attempt to normalize tests across docker installs and fix osx test setup --- .travis.yml | 2 +- integration-tests/goss/centos7/goss-expected.yaml | 2 -- integration-tests/goss/generate_goss.sh | 4 +++- integration-tests/goss/trusty/goss-expected.yaml | 2 -- integration-tests/goss/wheezy/goss-expected.yaml | 2 -- 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index f12e35e18..6a5395981 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ services: before_install: - if [[ "${TRAVIS_OS_NAME}" == "windows" ]]; then choco install make; fi # bash from macOS is too old to have readarray. Install newer version. - - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then brew install bash; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then brew update && brew install bash; fi install: - ./ci/install.sh diff --git a/integration-tests/goss/centos7/goss-expected.yaml b/integration-tests/goss/centos7/goss-expected.yaml index 0ca87a811..1d82b5da3 100644 --- a/integration-tests/goss/centos7/goss-expected.yaml +++ b/integration-tests/goss/centos7/goss-expected.yaml @@ -151,8 +151,6 @@ mount: opts: - rw - nosuid - - size=65536k - - mode=755 source: tmpfs filesystem: tmpfs http: diff --git a/integration-tests/goss/generate_goss.sh b/integration-tests/goss/generate_goss.sh index 433d03bf0..45b4fa8eb 100755 --- a/integration-tests/goss/generate_goss.sh +++ b/integration-tests/goss/generate_goss.sh @@ -59,8 +59,10 @@ goss a "${args[@]}" process $package foobar goss a "${args[@]}" kernel-param kernel.ostype goss a "${args[@]}" mount /dev -# Make SELinux test consistent +# Make tests consistent across different docker setups sed -i '/- seclabel/d' $SCRIPT_DIR/${OS}/goss-generated-$ARCH.yaml +sed -i '/- size=/d' $SCRIPT_DIR/${OS}/goss-generated-$ARCH.yaml +sed -i '/- mode=/d' $SCRIPT_DIR/${OS}/goss-generated-$ARCH.yaml goss a "${args[@]}" http https://www.google.com diff --git a/integration-tests/goss/trusty/goss-expected.yaml b/integration-tests/goss/trusty/goss-expected.yaml index c2c6cc353..9aad00c7d 100644 --- a/integration-tests/goss/trusty/goss-expected.yaml +++ b/integration-tests/goss/trusty/goss-expected.yaml @@ -151,8 +151,6 @@ mount: opts: - rw - nosuid - - size=65536k - - mode=755 source: tmpfs filesystem: tmpfs http: diff --git a/integration-tests/goss/wheezy/goss-expected.yaml b/integration-tests/goss/wheezy/goss-expected.yaml index f06545736..d500dff16 100644 --- a/integration-tests/goss/wheezy/goss-expected.yaml +++ b/integration-tests/goss/wheezy/goss-expected.yaml @@ -151,8 +151,6 @@ mount: opts: - rw - nosuid - - size=65536k - - mode=755 source: tmpfs filesystem: tmpfs http: From 28634ee4268e66ee0eecea827c721f7c32e2527d Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 24 Dec 2020 08:49:06 -0800 Subject: [PATCH 06/41] Try different osx image --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6a5395981..155f0f708 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ os: - windows dist: trusty +osx_image: xcode12.2 services: - docker @@ -21,7 +22,7 @@ services: before_install: - if [[ "${TRAVIS_OS_NAME}" == "windows" ]]; then choco install make; fi # bash from macOS is too old to have readarray. Install newer version. - - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then brew update && brew install bash; fi + - if [[ "${TRAVIS_OS_NAME}" == "osx" ]]; then brew install bash; fi install: - ./ci/install.sh From 1929d66375cc3ac6d36dfd48c160462b1b7c0e6e Mon Sep 17 00:00:00 2001 From: Peter Mounce Date: Sun, 27 Dec 2020 18:40:43 +0000 Subject: [PATCH 07/41] Rename binaries to strip '-alpha' (#671) * Rename binaries to strip '-alpha' As discussed in https://github.com/aelsabbahy/goss/pull/663#issuecomment-743127235; strip the -alpha naming, keep the messaging, keep the --use-alpha flag and env-var. This allows people to remove edge-cases from their installation, yet retains the clearly-set expectations of support being community-driven. Fixes #651. * set expectations for future-us re: macOS+Windows vs linux * fix doc --- .travis.yml | 8 ++++---- Makefile | 12 +++++------- cmd/goss/goss.go | 4 ++-- docs/platform-feature-parity.md | 11 ++++++----- integration-tests/goss/darwin/commands/add.goss.yaml | 2 +- .../goss/darwin/commands/autoadd.goss.yaml | 2 +- .../goss/darwin/commands/help.goss.yaml | 2 +- .../goss/darwin/commands/validate.goss.yaml | 2 +- .../goss/windows/commands/add.goss.yaml | 2 +- .../goss/windows/commands/autoadd.goss.yaml | 2 +- .../goss/windows/commands/help.goss.yaml | 2 +- .../goss/windows/commands/validate.goss.yaml | 2 +- integration-tests/run-serve-tests.sh | 4 ---- .../{run-tests-alpha.sh => run-validate-tests.sh} | 11 ++++++++--- release-build.sh | 4 ---- 15 files changed, 33 insertions(+), 37 deletions(-) rename integration-tests/{run-tests-alpha.sh => run-validate-tests.sh} (64%) diff --git a/.travis.yml b/.travis.yml index 155f0f708..6cb0abaf5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,16 +41,16 @@ deploy: token: secure: hEHCC4EN7iHz7pIWKRn2qw22NTqUxnuBp59wfAlLBtV26j5rHMzSu8mlxkJInusDUGLJiNLrZPRWN0mzOdIXalbUeLhlX7EflJgEj6Q0MchUR69LzCAp0KMIFL1Sfq0v81VgujRLUUy5utxDL8Er4tZknn2PpXAMzpO+ozjNRDhhSEM4iMXfY3bcOIMnx6XRgCjFCb036wlBgOfdgv5fwm2PP638DTKar4W6ZZbqCQByhJ5RyL3BMDPTT0moA/tYbG+FA6p6Rme1OcBkMnpsiJZoB3u8gxsNiEJ43/C2RcULW/18qqp2UVD5FipSDYP7GQ5ugKCbgpWXb0Ctl8o4hv1UsNl0XoyJhAt0PRp6vqnyy6LWB2FzX30Xj/vGIhO/IfiJvspHxpatTk7Esjr46K4u9ao/x63LX6F6yI1ZTfbzt2MhRYRjwh4ORNfqhysuzXChftX1S9hj6s6gO0/zqoOsRK/PK8DProbUn4bxrGOBzi16P0GEk4agWWUm74Pis9qCThXNW8MXEV936KvE1wb1RxTACYvFBtO2IM5eQ26t2Y7mGJd7FJup9LR4oUtUTSbYo5P2Sal6xntBKH5P4nwEtM+TtHoeSCKQ3X5i1VSdvAH7soEAly6rP5d5wwPhqqx9mgUPYO/3ulvxLJOYHamrbj6nlHDXnCEoj1ZMxX4= file: - - release/goss-alpha-darwin-amd64 - - release/goss-alpha-darwin-amd64.sha256 + - release/goss-darwin-amd64 + - release/goss-darwin-amd64.sha256 - release/goss-linux-amd64 - release/goss-linux-amd64.sha256 - release/goss-linux-386 - release/goss-linux-386.sha256 - release/goss-linux-arm - release/goss-linux-arm.sha256 - - release/goss-alpha-windows-amd64.exe - - release/goss-alpha-windows-amd64.exe.sha256 + - release/goss-windows-amd64.exe + - release/goss-windows-amd64.exe.sha256 - extras/dgoss/dgoss - extras/dgoss/dgoss.sha256 skip_cleanup: true diff --git a/Makefile b/Makefile index 3fb5a3dcb..a5e1aa8c1 100644 --- a/Makefile +++ b/Makefile @@ -36,15 +36,13 @@ bench: $(info INFO: Starting build $@) go test -bench=. -alpha-test-%: release/goss-% +test-int-validate-%: release/goss-% $(info INFO: Starting build $@) - ./integration-tests/run-tests-alpha.sh $* + ./integration-tests/run-validate-tests.sh $* test-int-serve-%: release/goss-% $(info INFO: Starting build $@) ./integration-tests/run-serve-tests.sh $* -# shim to account for linux being not in alpha -test-int-serve-linux-amd64: test-int-serve-alpha-linux-amd64 release/goss-%: $(GO_FILES) ./release-build.sh $* @@ -53,7 +51,7 @@ release: $(MAKE) clean $(MAKE) build -build: release/goss-alpha-darwin-amd64 release/goss-linux-386 release/goss-linux-amd64 release/goss-linux-arm release/goss-alpha-windows-amd64 +build: release/goss-darwin-amd64 release/goss-linux-386 release/goss-linux-amd64 release/goss-linux-arm release/goss-windows-amd64 gen: $(info INFO: Starting build $@) @@ -78,8 +76,8 @@ test-windows-all: test-short-all test-int-windows-all test-int-64: centos7 wheezy trusty alpine3 arch test-int-serve-linux-amd64 test-int-32: centos7-32 wheezy-32 trusty-32 alpine3-32 arch-32 -test-int-darwin-all: alpha-test-alpha-darwin-amd64 test-int-serve-alpha-darwin-amd64 -test-int-windows-all: alpha-test-alpha-windows-amd64 test-int-serve-alpha-windows-amd64 +test-int-darwin-all: test-int-validate-darwin-amd64 test-int-serve-darwin-amd64 +test-int-windows-all: test-int-validate-windows-amd64 test-int-serve-windows-amd64 test-int-all: test-int-32 test-int-64 centos7-32: build diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 633b5471c..02b326077 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -408,14 +408,14 @@ func addAlphaFlagIfNeeded(app *cli.App) { if runtime.GOOS == "darwin" || runtime.GOOS == "windows" { app.Flags = append(app.Flags, cli.StringFlag{ Name: "use-alpha", - Usage: fmt.Sprintf("goss is alpha-quality. Set to 1 to use anyway."), + Usage: fmt.Sprintf("goss on macOS/Windows is alpha-quality. Set to 1 to use anyway."), EnvVar: "GOSS_USE_ALPHA", Value: "0", }) } } -const msgFormat string = `WARNING: goss for this platform (%q) is alpha-quality, work-in-progress, and not yet exercised within continuous integration. +const msgFormat string = `WARNING: goss for this platform (%q) is alpha-quality, work-in-progress and community-supported. You should not expect everything to work. Treat linux as the canonical behaviour to expect. diff --git a/docs/platform-feature-parity.md b/docs/platform-feature-parity.md index 95ac5ee56..52ae4daca 100644 --- a/docs/platform-feature-parity.md +++ b/docs/platform-feature-parity.md @@ -4,7 +4,8 @@ macOS and Windows binaries are new and considered alpha-quality. Some functional To clearly signal that, goss emits a log message on every invocation saying so, linking here, then exits with a clear error. -To try out the alpha functionality, you must do one of +To try out the alpha functionality, you must do one of: + * pass `--use-alpha=1` to the root command - e.g. `goss --use-alpha=1 validate`. * set an environment variable `GOSS_USE_ALPHA=1`. @@ -140,8 +141,8 @@ You can find goss-files that are used to populate this matrix within `integratio Run all of the `darwin`/`windows` integration tests: ```bash -make alpha-test-alpha-darwin-amd64 -make alpha-test-alpha-windows-amd64 +make test-int-validate-darwin-amd64 +make test-int-validate-windows-amd64 ``` The script finds all goss spec files within `integration-tests` then filters to just ones matching the passed OS-name, then runs `validate` against them. @@ -153,11 +154,11 @@ This is a special-case test since it requires a persistent process, then to make #### macOS `serve` ```bash -make "test-serve-alpha-darwin-amd64" +make "test-int-serve-darwin-amd64" ``` #### Windows `serve` ```bash -make "test-serve-alpha-windows-amd64" +make "test-int-serve-windows-amd64" ``` diff --git a/integration-tests/goss/darwin/commands/add.goss.yaml b/integration-tests/goss/darwin/commands/add.goss.yaml index c07a27f90..cd75e38cc 100644 --- a/integration-tests/goss/darwin/commands/add.goss.yaml +++ b/integration-tests/goss/darwin/commands/add.goss.yaml @@ -3,7 +3,7 @@ command: "add addr 127.0.0.1": exit-status: 0 - exec: release/goss-alpha-darwin-amd64 --use-alpha=1 add addr 127.0.0.1 + exec: release/goss-darwin-amd64 --use-alpha=1 add addr 127.0.0.1 stdout: - "timeout: 500" stderr: [] diff --git a/integration-tests/goss/darwin/commands/autoadd.goss.yaml b/integration-tests/goss/darwin/commands/autoadd.goss.yaml index f2243cf92..830244ba4 100644 --- a/integration-tests/goss/darwin/commands/autoadd.goss.yaml +++ b/integration-tests/goss/darwin/commands/autoadd.goss.yaml @@ -2,7 +2,7 @@ command: "autoadd /Users/travis": exit-status: 0 - exec: "release/goss-alpha-darwin-amd64 --use-alpha=1 autoadd /Users/travis" + exec: "release/goss-darwin-amd64 --use-alpha=1 autoadd /Users/travis" stdout: - 'file:' - ' exists: true' diff --git a/integration-tests/goss/darwin/commands/help.goss.yaml b/integration-tests/goss/darwin/commands/help.goss.yaml index 7af8f2740..d57d2bb64 100644 --- a/integration-tests/goss/darwin/commands/help.goss.yaml +++ b/integration-tests/goss/darwin/commands/help.goss.yaml @@ -2,7 +2,7 @@ command: help: exit-status: 0 - exec: "release/goss-alpha-darwin-amd64 help" + exec: "release/goss-darwin-amd64 help" stdout: - alpha stderr: [] diff --git a/integration-tests/goss/darwin/commands/validate.goss.yaml b/integration-tests/goss/darwin/commands/validate.goss.yaml index edf22cad6..6d79a6d68 100644 --- a/integration-tests/goss/darwin/commands/validate.goss.yaml +++ b/integration-tests/goss/darwin/commands/validate.goss.yaml @@ -3,7 +3,7 @@ command: "validate": exit-status: 0 - exec: "release/goss-alpha-darwin-amd64 --use-alpha=1 -g integration-tests/goss/darwin/commands/validate-input.yaml validate" + exec: "release/goss-darwin-amd64 --use-alpha=1 -g integration-tests/goss/darwin/commands/validate-input.yaml validate" stdout: - 'Count: 1' - 'Failed: 0' diff --git a/integration-tests/goss/windows/commands/add.goss.yaml b/integration-tests/goss/windows/commands/add.goss.yaml index 8b4784895..d65d1899f 100644 --- a/integration-tests/goss/windows/commands/add.goss.yaml +++ b/integration-tests/goss/windows/commands/add.goss.yaml @@ -3,7 +3,7 @@ command: "add addr 127.0.0.1": exit-status: 0 - exec: release\goss-alpha-windows-amd64 --use-alpha=1 add addr 127.0.0.1 + exec: release\goss-windows-amd64 --use-alpha=1 add addr 127.0.0.1 stdout: - "timeout: 500" stderr: [] diff --git a/integration-tests/goss/windows/commands/autoadd.goss.yaml b/integration-tests/goss/windows/commands/autoadd.goss.yaml index cd88a70dc..5ed311a2b 100644 --- a/integration-tests/goss/windows/commands/autoadd.goss.yaml +++ b/integration-tests/goss/windows/commands/autoadd.goss.yaml @@ -2,7 +2,7 @@ command: "autoadd Administrator": exit-status: 0 - exec: release\goss-alpha-windows-amd64 --use-alpha=1 autoadd Administrator + exec: release\goss-windows-amd64 --use-alpha=1 autoadd Administrator stdout: - 'user:' - ' name: Administrator' diff --git a/integration-tests/goss/windows/commands/help.goss.yaml b/integration-tests/goss/windows/commands/help.goss.yaml index d1c5463bc..2f8bce527 100644 --- a/integration-tests/goss/windows/commands/help.goss.yaml +++ b/integration-tests/goss/windows/commands/help.goss.yaml @@ -2,7 +2,7 @@ command: help: exit-status: 0 - exec: release\goss-alpha-windows-amd64 help + exec: release\goss-windows-amd64 help stdout: - alpha stderr: [] diff --git a/integration-tests/goss/windows/commands/validate.goss.yaml b/integration-tests/goss/windows/commands/validate.goss.yaml index c504b5ec7..fe2456acd 100644 --- a/integration-tests/goss/windows/commands/validate.goss.yaml +++ b/integration-tests/goss/windows/commands/validate.goss.yaml @@ -3,7 +3,7 @@ command: "validate": exit-status: 0 - exec: "release\\goss-alpha-windows-amd64 --use-alpha=1 -g integration-tests/goss/windows/commands/validate-input.yaml validate" + exec: "release\\goss-windows-amd64 --use-alpha=1 -g integration-tests/goss/windows/commands/validate-input.yaml validate" stdout: - 'Count: 1' - 'Failed: 0' diff --git a/integration-tests/run-serve-tests.sh b/integration-tests/run-serve-tests.sh index 7e0390790..cd0301a0f 100755 --- a/integration-tests/run-serve-tests.sh +++ b/integration-tests/run-serve-tests.sh @@ -9,10 +9,6 @@ IFS='- ' read -r -a segments <<< "${platform_spec}" os="${segments[0]}" arch="${segments[1]}" -if [[ "${segments[0]}" == "alpha" ]]; then - os="${segments[1]}" - arch="${segments[2]}" -fi find_open_port() { local startAt="${1:?"Supply start of port range"}" diff --git a/integration-tests/run-tests-alpha.sh b/integration-tests/run-validate-tests.sh similarity index 64% rename from integration-tests/run-tests-alpha.sh rename to integration-tests/run-validate-tests.sh index 05676f050..90f85c207 100755 --- a/integration-tests/run-tests-alpha.sh +++ b/integration-tests/run-validate-tests.sh @@ -8,9 +8,14 @@ IFS='- ' read -r -a segments <<< "${platform_spec}" os="${segments[0]}" arch="${segments[1]}" -if [[ "${segments[0]}" == "alpha" ]]; then - os="${segments[1]}" - arch="${segments[2]}" + +if [[ "${os}" == "linux" ]]; then + echo "OS is ${os}. This script is not for running tests on the different flavours of linux." + echo "Linux is exercised via the integration-tests/test.sh currently, because linux can be" + echo "verified via docker containers; macOS and Windows cannot." + echo "This script is for macOS and Windows, and runs tests that are expected to pass on" + echo "Travis-CI provided images, running nakedly (no containerisation) on the hosts there." + exit 1 fi repo_root="$(git rev-parse --show-toplevel)" diff --git a/release-build.sh b/release-build.sh index b9d7b9d5e..c40d8bf6a 100755 --- a/release-build.sh +++ b/release-build.sh @@ -9,10 +9,6 @@ IFS='- ' read -r -a segments <<< "${platform_spec}" os="${segments[0]}" arch="${segments[1]}" -if [[ "${segments[0]}" == "alpha" ]]; then - os="${segments[1]}" - arch="${segments[2]}" -fi output_dir="release/" output_fname="goss-${platform_spec}" From bb7cb4726c5160373656503adac1c9e0a0941abd Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sun, 27 Dec 2020 13:22:03 -0800 Subject: [PATCH 08/41] F openrc runlevels (#668) * service: Add support for OpenRC runlevels * Omit empty runlevel * Update documentation * Update integration tests * Use consistent pattern for configu default values - Remove struct tag `default` value reflection * Make runLevels a testable attribute * empty commit to try to trigger travis * Update trusty to reflect precise changes Co-authored-by: Berney --- docs/manual.md | 5 +- integration-tests/Dockerfile_trusty | 3 +- integration-tests/goss/goss-shared.yaml | 7 ++ .../goss/trusty/goss-aa-expected.yaml | 2 +- .../goss/trusty/goss-expected-q.yaml | 2 +- .../goss/trusty/goss-expected.yaml | 2 +- integration-tests/goss/vars.yaml | 8 +++ integration-tests/test.sh | 2 +- resource/service.go | 24 ++++--- system/service.go | 1 + system/service_init.go | 66 ++++++++++++++----- system/service_systemd.go | 4 ++ system/service_upstart.go | 4 ++ util/config.go | 1 + 14 files changed, 101 insertions(+), 30 deletions(-) diff --git a/docs/manual.md b/docs/manual.md index 38b0156a5..9ae7b94ed 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -787,12 +787,15 @@ Validates the state of a service. ```yaml service: sshd: - # required attributes + # Optional attributes enabled: true running: true + runlevels: ["3", "4", "5"] # Alpine example, runlevels: ["default"] skip: false ``` +`runlevels` is only supported on Alpine init, sysv init, and upstart + **NOTE:** this will **not** automatically check if the process is alive, it will check the status from `systemd`/`upstart`/`init`. diff --git a/integration-tests/Dockerfile_trusty b/integration-tests/Dockerfile_trusty index 975b39eb0..04e0f8235 100644 --- a/integration-tests/Dockerfile_trusty +++ b/integration-tests/Dockerfile_trusty @@ -4,8 +4,7 @@ MAINTAINER Ahmed RUN apt-get update && \ apt-get install -y apache2=2.4.7-1ubuntu4.22 tinyproxy && \ apt-get remove -y vim-tiny && \ - apt-get clean && \ - echo manual > /etc/init/apache2.override + apt-get clean RUN sed -i '/reload|force-reload)/i status) pidof tinyproxy > /dev/null && echo "tinyproxy is running";;' /etc/init.d/tinyproxy RUN sed -i '/start)/a\ touch /var/log/tinyproxy/tinyproxy.log /var/run/tinyproxy/tinyproxy.pid' /etc/init.d/tinyproxy diff --git a/integration-tests/goss/goss-shared.yaml b/integration-tests/goss/goss-shared.yaml index ec0b47149..c3d1516ba 100644 --- a/integration-tests/goss/goss-shared.yaml +++ b/integration-tests/goss/goss-shared.yaml @@ -62,6 +62,13 @@ package: versions: - {{$ver}} {{end}} +service: +{{- range $name, $runlevels := index .Vars .Env.OS "services"}} + {{$name}}: + enabled: true + running: true + runlevels: {{toJson $runlevels}} +{{end}} addr: tcp://google.com:22: reachable: false diff --git a/integration-tests/goss/trusty/goss-aa-expected.yaml b/integration-tests/goss/trusty/goss-aa-expected.yaml index a9a98ed27..8be205cea 100644 --- a/integration-tests/goss/trusty/goss-aa-expected.yaml +++ b/integration-tests/goss/trusty/goss-aa-expected.yaml @@ -10,7 +10,7 @@ port: - 0.0.0.0 service: apache2: - enabled: false + enabled: true running: true process: apache2: diff --git a/integration-tests/goss/trusty/goss-expected-q.yaml b/integration-tests/goss/trusty/goss-expected-q.yaml index c1e6e053a..e37715a76 100644 --- a/integration-tests/goss/trusty/goss-expected-q.yaml +++ b/integration-tests/goss/trusty/goss-expected-q.yaml @@ -31,7 +31,7 @@ port: listening: false service: apache2: - enabled: false + enabled: true running: true foobar: enabled: false diff --git a/integration-tests/goss/trusty/goss-expected.yaml b/integration-tests/goss/trusty/goss-expected.yaml index 9aad00c7d..6310a6656 100644 --- a/integration-tests/goss/trusty/goss-expected.yaml +++ b/integration-tests/goss/trusty/goss-expected.yaml @@ -42,7 +42,7 @@ port: ip: [] service: apache2: - enabled: false + enabled: true running: true foobar: enabled: false diff --git a/integration-tests/goss/vars.yaml b/integration-tests/goss/vars.yaml index ec08626b9..66a205c68 100644 --- a/integration-tests/goss/vars.yaml +++ b/integration-tests/goss/vars.yaml @@ -3,19 +3,27 @@ alpine3: proxy: http://127.0.0.1:8888 packages: apache2: "2.4.46-r1" + services: + apache2: [sysinit] arch: packages: centos7: proxy: http://127.0.0.1:8888 packages: httpd: "2.4.6" + services: + httpd: [] trusty: proxy: http://127.0.0.1:8888 packages: apache2: "2.4.7-1ubuntu4.22" + services: + apache2: ["3"] wheezy: proxy: http://127.0.0.1:8888 packages: apache2: "2.2.22-13+deb7u13" + services: + apache2: ["2", "3", "5", "4"] overwrite: foo diff --git a/integration-tests/test.sh b/integration-tests/test.sh index 59f8f1ac7..742ee271c 100755 --- a/integration-tests/test.sh +++ b/integration-tests/test.sh @@ -44,7 +44,7 @@ echo "$out" if [[ $os == "arch" ]]; then egrep -q 'Count: 97, Failed: 0, Skipped: 3' <<<"$out" else - egrep -q 'Count: 117, Failed: 0, Skipped: 5' <<<"$out" + egrep -q 'Count: 118, Failed: 0, Skipped: 5' <<<"$out" fi if [[ ! $os == "arch" ]]; then diff --git a/resource/service.go b/resource/service.go index 5052707ff..d8326f8fb 100644 --- a/resource/service.go +++ b/resource/service.go @@ -6,12 +6,13 @@ import ( ) type Service struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Service string `json:"-" yaml:"-"` - Enabled matcher `json:"enabled" yaml:"enabled"` - Running matcher `json:"running" yaml:"running"` - Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + Service string `json:"-" yaml:"-"` + Enabled matcher `json:"enabled" yaml:"enabled"` + Running matcher `json:"running" yaml:"running"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + RunLevels matcher `json:"runlevels,omitempty" yaml:"runlevels,omitempty"` } func (s *Service) ID() string { return s.Service } @@ -29,8 +30,15 @@ func (s *Service) Validate(sys *system.System) []TestResult { } var results []TestResult - results = append(results, ValidateValue(s, "enabled", s.Enabled, sysservice.Enabled, skip)) - results = append(results, ValidateValue(s, "running", s.Running, sysservice.Running, skip)) + if s.Enabled != nil { + results = append(results, ValidateValue(s, "enabled", s.Enabled, sysservice.Enabled, skip)) + } + if s.Running != nil { + results = append(results, ValidateValue(s, "running", s.Running, sysservice.Running, skip)) + } + if s.RunLevels != nil { + results = append(results, ValidateValue(s, "runlevels", s.RunLevels, sysservice.RunLevels, skip)) + } return results } diff --git a/system/service.go b/system/service.go index 79cbdcf30..df2a43dc7 100644 --- a/system/service.go +++ b/system/service.go @@ -7,6 +7,7 @@ type Service interface { Exists() (bool, error) Enabled() (bool, error) Running() (bool, error) + RunLevels() ([]string, error) } func invalidService(s string) bool { diff --git a/system/service_init.go b/system/service_init.go index 030296476..0126c55a8 100644 --- a/system/service_init.go +++ b/system/service_init.go @@ -4,13 +4,15 @@ import ( "fmt" "os" "path/filepath" + "regexp" "github.com/aelsabbahy/goss/util" ) type ServiceInit struct { - service string - alpine bool + service string + alpine bool + runlevel string } func NewServiceInit(service string, system *System, config util.Config) Service { @@ -18,7 +20,11 @@ func NewServiceInit(service string, system *System, config util.Config) Service } func NewAlpineServiceInit(service string, system *System, config util.Config) Service { - return &ServiceInit{service: service, alpine: true} + runlevel := config.RunLevel + if runlevel == "" { + runlevel = "sysinit" + } + return &ServiceInit{service: service, alpine: true, runlevel: runlevel} } func (s *ServiceInit) Service() string { @@ -39,10 +45,24 @@ func (s *ServiceInit) Enabled() (bool, error) { if invalidService(s.service) { return false, nil } + var runLevels []string + var err error + if s.alpine { + runLevels, err = alpineServiceRunLevels(s.service) + } else { + runLevels, err = initServiceRunLevels(s.service) + } + return len(runLevels) != 0, err +} + +func (s *ServiceInit) RunLevels() ([]string, error) { + if invalidService(s.service) { + return nil, nil + } if s.alpine { - return alpineInitServiceEnabled(s.service, "sysinit") + return alpineServiceRunLevels(s.service) } else { - return initServiceEnabled(s.service, 3) + return initServiceRunLevels(s.service) } } @@ -58,18 +78,34 @@ func (s *ServiceInit) Running() (bool, error) { return false, nil } -func initServiceEnabled(service string, level int) (bool, error) { - matches, err := filepath.Glob(fmt.Sprintf("/etc/rc%d.d/S[0-9][0-9]%s", level, service)) - if err == nil && matches != nil { - return true, nil +func initServiceRunLevels(service string) ([]string, error) { + var runLevels []string + matches, err := filepath.Glob(fmt.Sprintf("/etc/rc*.d/S[0-9][0-9]%s", service)) + if err != nil { + return nil, err } - return false, err + re := regexp.MustCompile("/etc/rc([0-9]+).d/") + for _, m := range matches { + matches := re.FindStringSubmatch(m) + if matches != nil { + runLevels = append(runLevels, matches[1]) + } + } + return runLevels, nil } -func alpineInitServiceEnabled(service string, level string) (bool, error) { - matches, err := filepath.Glob(fmt.Sprintf("/etc/runlevels/%s/%s", level, service)) - if err == nil && matches != nil { - return true, nil +func alpineServiceRunLevels(service string) ([]string, error) { + var runLevels []string + matches, err := filepath.Glob(fmt.Sprintf("/etc/runlevels/*/%s", service)) + if err != nil { + return nil, err + } + re := regexp.MustCompile("/etc/runlevels/([^/]+)") + for _, m := range matches { + matches := re.FindStringSubmatch(m) + if matches != nil { + runLevels = append(runLevels, matches[1]) + } } - return false, err + return runLevels, nil } diff --git a/system/service_systemd.go b/system/service_systemd.go index cfc183eaf..8d0b903bc 100644 --- a/system/service_systemd.go +++ b/system/service_systemd.go @@ -85,3 +85,7 @@ func (s *ServiceSystemd) Running() (bool, error) { } return false, nil } + +func (s *ServiceSystemd) RunLevels() ([]string, error) { + return nil, nil +} diff --git a/system/service_upstart.go b/system/service_upstart.go index a65c9fd00..7c05ec903 100644 --- a/system/service_upstart.go +++ b/system/service_upstart.go @@ -77,3 +77,7 @@ func (s *ServiceUpstart) Running() (bool, error) { } return false, nil } +func (s *ServiceUpstart) RunLevels() ([]string, error) { + sysv := &ServiceInit{service: s.service} + return sysv.RunLevels() +} diff --git a/util/config.go b/util/config.go index f92b8c431..696308899 100644 --- a/util/config.go +++ b/util/config.go @@ -42,6 +42,7 @@ type Config struct { Proxy string RequestHeader []string RetryTimeout time.Duration + RunLevel string Server string Sleep time.Duration Spec string From 427348a3da2ec37defe596cf10e3b7589aaabf5c Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Wed, 30 Dec 2020 08:58:35 -0800 Subject: [PATCH 09/41] POC/DRAFT: Add transforms (#576) * Add transforms * wip add transforms work * Add some examples to make sense of this * Handle floats/ints better * Convert validatecontails to gomega * wip * Add gjson and contain-substring * wip * Subclass all matchers adding String() * I don't evne know what this is.. should have checked it in back when I wrote it * Migrate GomegaMatcher -> GossMatcher * lots of changes + drop json-iterator * Move output related logic to outputs * Add include_raw flag * Add bench output format * Update have_patterns to check input. Add stubs to make Not matcher act as omegaMatcher * SetEscapeHTML(false) for semver constraint * Ensure have-Key_with_value works * remove have-key-with-value * Fix error compact output. Add oMegaMatcher stubs. Ensure matcher vs m consistent * Cleanup matchers * Cleanup TestResult struct and remove need for dyno * Update all deps * Update tranformer tests and add summary line to bench output format * Update readme * Conditionally print missing and empty values. Sanitize found and expected values for consistency * Check value is valid before checking IsNil() * Initial docs change * WIP fix for pretty print * Swap order on video vs blog for dgoss docs * Updated all tests except for semver * Fix all unit tests * Fix detection of when matcher is set * Update documentation * Update docs/manual.md Co-authored-by: Peter Mounce * Update docs/manual.md Co-authored-by: Peter Mounce * gjson: Validate json before doing gets * Make errors more clear * Remove unused field * Bump go version * Update doc * Fix regression with gomega errors * Fix #262 no need to add file size by default * Fix output formats * Use proper quoting for windows test * Add error checking for gjson path not found Co-authored-by: Peter Mounce --- .travis.yml | 2 +- README.md | 4 +- docs/manual.md | 315 +++++++++++++--- examples/goss.yaml | 25 ++ examples/goss_awesome_gomega.yaml | 136 +++++++ examples/readme.md | 3 + examples/test.txt | 2 + extras/dgoss/README.md | 4 +- go.mod | 17 +- go.sum | 48 ++- goss_test.go | 5 +- .../goss/alpine3/goss-expected.yaml | 1 - .../goss/centos7/goss-expected.yaml | 1 - integration-tests/goss/goss-shared.yaml | 2 - .../goss/trusty/goss-expected.yaml | 1 - .../goss/wheezy/goss-expected.yaml | 1 - .../goss/windows/tests/command.goss.yaml | 4 +- matchers/and.go | 50 +++ matchers/be_numerically_matcher.go | 50 +++ matchers/consist_of.go | 45 +++ matchers/contain_element_matcher.go | 41 ++ matchers/contain_elements_matcher.go | 43 +++ matchers/contain_substring_matcher.go | 42 +++ matchers/equal_matcher.go | 39 ++ matchers/have_key_matcher.go | 41 ++ matchers/have_len_matcher.go | 41 ++ matchers/have_patterns.go | 254 +++++++++++++ matchers/have_prefix_matcher.go | 42 +++ matchers/have_suffix_matcher.go | 42 +++ matchers/match_regexp_matcher.go | 42 +++ matchers/matchers.go | 47 +++ matchers/not.go | 36 ++ matchers/or.go | 52 +++ matchers/semver_constraint.go | 52 ++- matchers/semver_constraint_test.go | 63 ++-- matchers/type_conversion.go | 138 +++++++ matchers/with_safe_transform.go | 82 ++++ outputs/bench.go | 39 ++ outputs/documentation.go | 9 +- outputs/json.go | 6 +- outputs/json_oneline.go | 59 --- outputs/junit.go | 7 +- outputs/nagios.go | 3 +- outputs/outputs.go | 154 +++++--- outputs/rspecish.go | 4 +- outputs/structured.go | 11 +- outputs/tap.go | 7 +- resource/command.go | 26 +- resource/file.go | 39 +- resource/gomega.go | 128 +++---- resource/gomega_test.go | 128 +++---- resource/http.go | 14 +- resource/matching.go | 26 +- resource/resource.go | 17 +- resource/validate.go | 353 ++++-------------- resource/validate_test.go | 58 +-- store.go | 13 +- validate.go | 6 + 58 files changed, 2111 insertions(+), 809 deletions(-) create mode 100644 examples/goss.yaml create mode 100644 examples/goss_awesome_gomega.yaml create mode 100644 examples/readme.md create mode 100644 examples/test.txt create mode 100644 matchers/and.go create mode 100644 matchers/be_numerically_matcher.go create mode 100644 matchers/consist_of.go create mode 100644 matchers/contain_element_matcher.go create mode 100644 matchers/contain_elements_matcher.go create mode 100644 matchers/contain_substring_matcher.go create mode 100644 matchers/equal_matcher.go create mode 100644 matchers/have_key_matcher.go create mode 100644 matchers/have_len_matcher.go create mode 100644 matchers/have_patterns.go create mode 100644 matchers/have_prefix_matcher.go create mode 100644 matchers/have_suffix_matcher.go create mode 100644 matchers/match_regexp_matcher.go create mode 100644 matchers/matchers.go create mode 100644 matchers/not.go create mode 100644 matchers/or.go create mode 100644 matchers/type_conversion.go create mode 100644 matchers/with_safe_transform.go create mode 100644 outputs/bench.go delete mode 100644 outputs/json_oneline.go diff --git a/.travis.yml b/.travis.yml index 6cb0abaf5..1e9ebd107 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ env: language: go go: - - 1.13.x + - 1.15.x os: - osx diff --git a/README.md b/README.md index 9cc99b826..6c4d5e5ad 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,7 @@ asciicast -**Note:** For an even faster way of doing this, see: [autoadd](https://github.com/aelsabbahy/goss/blob/master/docs/manual.md#autoadd-aa---auto-add-all-matching-resources-to-test-suite) - -**Note:** For testing docker containers see the [dgoss](https://github.com/aelsabbahy/goss/tree/master/extras/dgoss) wrapper. Also, user submitted wrapper scripts for Kubernetes [kgoss](https://github.com/aelsabbahy/goss/tree/master/extras/kgoss) and Docker Compose [dcgoss](https://github.com/aelsabbahy/goss/tree/master/extras/dcgoss). +**Note:** For testing docker containers see the [dgoss](https://github.com/aelsabbahy/goss/tree/master/extras/dgoss) wrapper. Also, see [extras/](https://github.com/aelsabbahy/goss/tree/master/extras) for other user submitted wrapper scrips ([Kubernetes](https://github.com/aelsabbahy/goss/tree/master/extras/kgoss), [Docker Compose](https://github.com/aelsabbahy/goss/tree/master/extras/dcgoss)) **Note:** For some Docker/Kubernetes healthcheck, health endpoint, and container ordering examples, see my blog post diff --git a/docs/manual.md b/docs/manual.md index 9ae7b94ed..e8fc0b7ac 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -33,7 +33,7 @@ * [process](#process) * [service](#service) * [user](#user) -* [Patterns](#patterns) +* [Matchers](#matchers) * [Advanced Matchers](#advanced-matchers) * [Templates](#templates) @@ -461,7 +461,7 @@ addr: ### command -Validates the exit-status and output of a command +Validates the exit-status and output of a command. This can be used in combination with the [gjson](#gjson) matcher to create powerful goss custom tests. ```yaml command: @@ -730,8 +730,6 @@ matching: baz: bing matches: and: - - have-key-with-value: - foo: bar - have-key: baz ``` @@ -820,91 +818,294 @@ user: **NOTE:** This check is inspecting the contents of local passwd file `/etc/passwd`, this does not validate remote users (e.g. LDAP). -## Patterns -For the attributes that use patterns (ex. `file`, `command` `output`), each pattern is checked against the attribute string, the type of patterns are: +## Matchers -* `"string"` - checks if any line contain string. -* `"!string"` - inverse of above, checks that no line contains string -* `"\\!string"` - escape sequence, check if any line contains `"!string"` -* `"/regex/"` - verifies that line contains regex -* `"!/regex/"` - inverse of above, checks that no line contains regex +### Default Matchers -**NOTE:** Pattern attributes do not support [Advanced Matchers](#advanced-matchers) +Default matchers are determined by the attribute value received from the system. -**NOTE:** Regex support is based on golang's regex engine documented [here](https://golang.org/pkg/regexp/syntax/) +#### Bool, Strings, Integers + +Bool, Strings and integers are compared using equality, for example: + +```yaml +matching: + basic_string: + content: 'foo' + matches: 'foo' + +user: + nfsnobody: + exists: true + uid: 65534 +``` + +#### Arrays + +Arrays are treated as a [contains-elements](#array-matchers) by default, this validates that the expected test is a subset of the returned system state. + +```yaml +matching: + basic_array: + content: + - 'group1' + - 'group2' + - 'group3' + matches: + - 'group1' + - 'group2' + + # This fails, since the returned result and it's no longer a subset + basic_array_failing: + content: + - 'group1' + - 'group2' + - 'group3' + matches: + - 'group1' + - 'group2' + - 'group2' # this 2nd group2 is not in the returned content +``` + +#### io.Readers + +This is the most magical matcher for goss. It remains a default for historic and performance reasons. Some attributes return an io.Reader that is read line by line (ex. file content, command, http body). This allows goss to validate large files/content efficiently. + + +Each pattern is checked against the attribute output, the type of patterns are: + +* `"foo"` - checks if any line contains `foo` +* `"!foo"` - inverse of above, checks that no line contains `foo` +* `"\\!foo"` - escape sequence, check if any line contains `!string` +* `"/[Rr]egex/"` - verifies that line matches regex +* `"!/[Rr]egex/"` - inverse of above, checks that no line matches regex + +**NOTE:** Regex support is based on Golang's regex engine documented [here](https://golang.org/pkg/regexp/syntax/) **NOTE:** You will **need** the double backslash (`\\`) escape for Regex special entities, for example `\\s` for blank spaces. -### Example -```bash -$ cat /tmp/test.txt -found -!alsofound +Example: +```yaml +file: + /tmp/test.txt: + exists: true + contains: + - "foo" + - "!bar" + - "/[Gg]oss/" +``` -$ cat goss.yaml +The above can be expressed as: + +```yaml file: /tmp/test.txt: exists: true contains: - - found - - /fou.d/ - - "\\!alsofound" - - "!missing" - - "!/mis.ing/" + and: + - contain-element: "foo" + - not: {contain-element: "bar"} + - contain-element: {match-regexp: "[Gg]oss"} + +``` + +### Transforms + +If the system state type and the expected type don't match, goss will attempt to transform the system state type before matching it. + +For example, kernel-param attribute returns a string, however, it can be tested using numeric comparisons: + +Example kernel-param test: +```yaml +kernel-param: + net.core.somaxconn: + value: "128" +``` + +Example (failing) kernel-param test with transform: +```yaml +kernel-param: + net.core.somaxconn: + value: {gt: 200} +``` + +When a transformed test fails, it will detail the transformers used, the `-o include_raw` option can be used to include the raw, untransformed attribute value: +``` +$ goss v +F -$ goss validate -.. +Failures/Skipped: + +KernelParam: net.core.somaxconn: value: +Expected + 128 +to be > + 200 +the transform chain was + [{"to-numeric":{}}] + +Total Duration: 0.001s +Count: 1, Failed: 1, Skipped: 0 + + +$ goss v -o include_raw +F + +Failures/Skipped: + +KernelParam: net.core.somaxconn: value: +Expected + 128 +to be > + 200 +the transform chain was + [{"to-numeric":{}}] +the raw value was + "128" Total Duration: 0.001s -Count: 2, Failed: 0 +Count: 1, Failed: 1, Skipped: 0 + ``` -## Advanced Matchers -Goss supports advanced matchers by converting json input to [gomega](https://onsi.github.io/gomega/) matchers. +### Advanced Matchers -### Examples +Goss supports advanced matchers by converting YAML input to [gomega](https://onsi.github.io/gomega/) matchers. + +#### String Matchers + +These will convert the system attribute to a string prior to matching. -Validate that user `nobody` has a `uid` that is less than `500` and that they are **only** a member of the `nobody` group. +* `'55'` - Checks that the numeric is "55" when converted to string +* `have-prefix: pre` - Checks if string starts with "pre" +* `have-suffix: suf` - Checks if string ends with "suf" +* `match-regexp: '.*'` - Checks if string matches regexp +* `contain-substring: '2'` - Checks if string contains "2" +Example: ```yaml -user: - nobody: - exists: true - uid: - lt: 500 - groups: - consist-of: [nobody] +matching: + example: + content: 42 + matches: + and: + - '42' + - have-prefix: '4' + - have-suffix: '2' + - match-regexp: '\d{2}' + - contain-substring: '2' ``` -Matchers can be nested for more complex logic, for example you can ensure that you have 3 kernel versions installed and none of them are `4.1.0`: +#### Numeric matchers + +These will convert the system attribute to a numeric prior to matching. +* `42` - If the expected type is a number +* `gt, ge, lt, le` - Greater than, greater than or equal, less than, etc.. + +Example: ```yaml -package: - kernel: - installed: true - versions: +matching: + example: + content: "42" + matches: and: - - have-len: 3 - - not: - contain-element: "4.1.0" + - 42 + - 42.0 + - gt: 40 + - lt: 45 ``` -Custom semver matcher is available under `semver-constraint`: +#### Array matchers + +These will convert the system attribute to an array prior to matching. Strings are split on "\n" + + +* `contain-element: matcher` - Checks if the array contains an element that passes the matcher +* `contain-elements: [matcher, ...]` - checks if the array is a superset of the provided matchers +* `[matcher, ...]` - same as above +* `equal: [value, ...]` - Checks if the array is exactly equal to provided array +* `consist-of: [matcher, ...]` - Checks if the array consists of the provided matchers (order does not matter) +Example: ```yaml -example: - content: - - 1.0.1 - - 1.9.9 - matches: - semver-constraint: ">1.0.0 <2.0.0 !=1.5.0" +matching: + example: + content: [foo, bar, moo] + matches: + and: + - contain-elements: [foo, bar] + - [foo, bar] # same as above + - equal: [foo, bar, moo] # order matters, exact match + - consist-of: [foo, have-prefix: m, bar] # order doesn't matter, can use matchers + - contain-element: + have-prefix: b +``` + +#### Misc matchers + +These matchers don't really fall into any of the above categories, or span multiple categories. + +* `equal` - Useful when needing to override a default matcher +* `have-len: 3` - Checks if the array/string/map has length of 3 +* `have-key: "foo"` - Checks if key exists in map, useful with `gjson` +* `not: matcher` - Checks that a matcher does not match +* `and: [matcher, ..]` - Checks that all matchers match +* `or: [matcher, ..]` - Checks that any matchers match + * when system returns a string it is converted into a one element array and matched + +See the following for examples: [link..]fixme + +##### semver-constraint + +Checks that all versions match semver constraint or range syntax. This uses [semver](https://github.com/blang/semver) under the hood, however, wildcards (e.g. `1.X` are not officially supported and may go away in a future release. + +Example: +```yaml +matching: + semver: + content: + - 1.0.1 + - 1.9.9 + matches: + semver-constraint: ">1.0.0 <2.0.0 !=1.5.0" + semver2: + content: + - 1.0.1 + - 1.5.0 + - 1.9.9 + matches: + not: + semver-constraint: ">1.0.0 <2.0.0 !=1.5.0" + semver3: + content: 1.0.1 + matches: + semver-constraint: ">5.0.0 || < 1.5.0" +``` + + +##### gjson + +Checks extracted [gjson](https://gjson.dev/) passes the matcher + +Example: +```yaml +matching: + example: + content: '{"foo": "bar", "moo" {"nested": "cow"}, "count": "15"}' + matches: + gjson: + moo.nested: cow + foo: {have-prefix: b} + count: {le: 25} + '@this': {have-key: "foo"} + moo: + and: + - {have-key: "nested"} + - {not: {have-key: "nested2"}} ``` -For more information see: -* [gomega_test.go](https://github.com/aelsabbahy/goss/blob/master/resource/gomega_test.go) - For a complete set of supported json -> Gomega mapping -* [gomega](https://onsi.github.io/gomega/) - Gomega matchers reference -* [semver](https://github.com/blang/semver#ranges) - Semver constraint (or range) syntax ## Templates diff --git a/examples/goss.yaml b/examples/goss.yaml new file mode 100644 index 000000000..1265c8490 --- /dev/null +++ b/examples/goss.yaml @@ -0,0 +1,25 @@ +gossfile: + goss_awesome_gomega.yaml: {} + +file: + test.txt: + exists: true + contains: | + test file + second line + +command: + echo '15': + exit-status: 0 + stdout: + and: + - gt: 10 + - lt: 50 + - match-regexp: '\d{2}' + timeout: 10000 + +http: + https://ifconfig.me: + status: 200 + timeout: 5000 + body: '{{.Vars.Ip}}' diff --git a/examples/goss_awesome_gomega.yaml b/examples/goss_awesome_gomega.yaml new file mode 100644 index 000000000..46a693e11 --- /dev/null +++ b/examples/goss_awesome_gomega.yaml @@ -0,0 +1,136 @@ +matching: + # Basic matchers + basic_string: + content: 'this is a test' + matches: 'this is a test' + + basic_int: + content: 42 + matches: 42 + + basic_array: + content: + - 'group1' + - 'group2' + - 'group3' + matches: + - 'group1' + - 'group2' + + basic_reader: + as-reader: true + content: | + foo bar + moo cow + matches: + - 'foo' + - '/^m.*w$/' + - '!wtf' + - '!/^ERROR:/' + + basic_reader_as_array: + as-reader: true + content: | + foo bar + moo cow + matches: + and: + - contain-element: {contain-substring: 'foo'} + - contain-element: {match-regexp: '^m.*w$'} + - not: {contain-substring: 'wtf'} + - not: {match-regexp: '^ERROR:'} + + # Transformers + test_numeric_string: + content: 128 + matches: + and: + - '128' + - have-prefix: '1' + - have-suffix: '8' + - match-regexp: '\d{3}' + + test_string_numeric: + content: '128' + matches: + and: + - 128 + - 128.0 + - le: 128 + - gt: 120 + + test_string_float: + content: '128.3' + matches: + and: + - 128.3 + - le: 129 + - gt: 120.2 + + test_array: + content: + - '46' + - '45' + matches: + - and: [{ge: 40}, {le: 50}] + - '46' + + test_reader_using_string_matchers: + content: | + foo bar + 15 + moo cow + as-reader: true + matches: + and: + - have-len: 19 + - | + foo bar + 15 + moo cow + - have-prefix: 'foo' + - have-suffix: "cow\n" + - contain-element: + have-prefix: 'moo' + - contain-elements: + - not: 'this_doesnt_exist' + - lt: 20 + - have-prefix: 'moo' + + + test_reader_as_single_string: + content: 'cool' + as-reader: true + matches: 'cool' + + test_reader_using_int_matchers: + content: '40' + as-reader: true + matches: + and: + - le: 250 + - ge: 20 + + + test_gjson_transform: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + moo.nested: cow + foo: {have-prefix: b} + count: {le: 25} + '@this': {have-key: "foo"} + moo: + and: + - {have-key: "nested"} + - {not: {have-key: "nested2"}} + + test_gjson_using_this_and_equal: + content: '{"foo": "bar", "baz": "bing"}' + matches: + gjson: + '@this': + equal: + foo: bar + baz: bing diff --git a/examples/readme.md b/examples/readme.md new file mode 100644 index 000000000..350a9df18 --- /dev/null +++ b/examples/readme.md @@ -0,0 +1,3 @@ +# How to run this + +Basically, run the following: `goss --vars-inline "Ip: $EXTERNAL_IP" v` diff --git a/examples/test.txt b/examples/test.txt new file mode 100644 index 000000000..ae7cb70bc --- /dev/null +++ b/examples/test.txt @@ -0,0 +1,2 @@ +test file +second line diff --git a/extras/dgoss/README.md b/extras/dgoss/README.md index 64d045d89..132051244 100644 --- a/extras/dgoss/README.md +++ b/extras/dgoss/README.md @@ -3,8 +3,8 @@ dgoss is a convenience wrapper around goss that aims to bring the simplicity of goss to docker containers. ## Examples and Tutorials -* [video tutorial](https://youtu.be/PEHz5EnZ-FM) - Introduction to dgoss tutorial -* [blog tutorial](https://medium.com/@aelsabbahy/tutorial-how-to-test-your-docker-image-in-half-a-second-bbd13e06a4a9) - Same as above, but in written format +* [blog tutorial](https://medium.com/@aelsabbahy/tutorial-how-to-test-your-docker-image-in-half-a-second-bbd13e06a4a9) - Introduction to dgoss tutorial +* [video tutorial](https://youtu.be/PEHz5EnZ-FM) - Same as above, but in video format * [dgoss-examples](https://github.com/aelsabbahy/dgoss-examples) - Repo containing examples of using dgoss to validate docker images ## Installation diff --git a/go.mod b/go.mod index 4bfaf5a6b..abcca3182 100644 --- a/go.mod +++ b/go.mod @@ -11,20 +11,23 @@ require ( github.com/cheekybits/genny v1.0.0 github.com/fatih/color v1.10.0 github.com/go-ole/go-ole v1.2.4 // indirect - github.com/google/uuid v1.1.1 // indirect - github.com/huandu/xstrings v1.3.0 // indirect - github.com/imdario/mergo v0.3.8 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/icza/dyno v0.0.0-20200205103839-49cb13720835 + github.com/imdario/mergo v0.3.11 // indirect github.com/miekg/dns v1.1.35 github.com/mitchellh/copystructure v1.0.0 // indirect github.com/moby/sys/mountinfo v0.4.0 - github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f + github.com/oleiade/reflections v1.0.1 github.com/onsi/gomega v1.10.4 - github.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a + github.com/opencontainers/runc v0.1.1 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/shirou/gopsutil v2.20.6+incompatible + github.com/shirou/gopsutil v3.20.11+incompatible github.com/stretchr/testify v1.6.1 github.com/thoas/go-funk v0.7.0 - github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 + github.com/tidwall/gjson v1.6.7 + github.com/urfave/cli v1.22.5 + golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index e73739a68..a0e3854c9 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= @@ -14,6 +15,8 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= @@ -34,13 +37,15 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo= -github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= +github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/icza/dyno v0.0.0-20200205103839-49cb13720835 h1:f1irK5f03uGGj+FjgQfZ5VhdKNVQVJ4skHsedzVohQ4= +github.com/icza/dyno v0.0.0-20200205103839-49cb13720835/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk= +github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -55,34 +60,45 @@ github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f h1:I6mXuorHlvwNDFelz7a+j0HaGYSzX7+Gq60DqLVypfc= -github.com/oleiade/reflections v0.0.0-20160817071559-0e86b3c98b2f/go.mod h1:RbATFBbKYkVdqmSFtx13Bb/tVhR0lgOBXunWTZKeL4w= +github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM= +github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.4 h1:NiTx7EEvBzu9sFOD1zORteLSt3o8gnlvZZwSE9TnY9U= github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= -github.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a h1:wvqfuQnd/Z9zDxpkyaabmBVf/3RYUmn4B4A1mG0pf3I= -github.com/opencontainers/runc v0.0.0-20161107232042-8779fa57eb4a/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.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/shirou/gopsutil v2.20.6+incompatible h1:P37G9YH8M4vqkKcwBosp+URN5O8Tay67D2MbR361ioY= -github.com/shirou/gopsutil v2.20.6+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto= +github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/thoas/go-funk v0.7.0 h1:GmirKrs6j6zJbhJIficOsz2aAI7700KsU/5YrdHRM1Y= github.com/thoas/go-funk v0.7.0/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= -github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13 h1:niRuEF0NOlFnqraxzjuvvOdCM6gxmHiaBABjvg3/kDo= -github.com/urfave/cli v0.0.0-20161102131801-d86a009f5e13/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/tidwall/gjson v1.6.7 h1:Mb1M9HZCRWEcXQ8ieJo7auYyyiSux6w9XN3AdTpxJrE= +github.com/tidwall/gjson v1.6.7/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= +golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -98,12 +114,14 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/goss_test.go b/goss_test.go index a1328dcb8..5af7cffdf 100644 --- a/goss_test.go +++ b/goss_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/aelsabbahy/goss/outputs" + "github.com/aelsabbahy/goss/resource" "github.com/aelsabbahy/goss/util" ) @@ -85,7 +86,7 @@ func TestUseAsPackage(t *testing.T) { for _, r := range rg { found++ - if r.Successful { + if r.Result == resource.SUCCESS { passed++ } } @@ -111,7 +112,7 @@ func TestUseAsPackage(t *testing.T) { okcount := 0 for _, r := range res.Results { - if r.Successful { + if r.Result == resource.SUCCESS { okcount++ } } diff --git a/integration-tests/goss/alpine3/goss-expected.yaml b/integration-tests/goss/alpine3/goss-expected.yaml index 99cef1789..cb175b59d 100644 --- a/integration-tests/goss/alpine3/goss-expected.yaml +++ b/integration-tests/goss/alpine3/goss-expected.yaml @@ -2,7 +2,6 @@ file: /etc/passwd: exists: true mode: "0644" - size: 1287 owner: root group: root filetype: file diff --git a/integration-tests/goss/centos7/goss-expected.yaml b/integration-tests/goss/centos7/goss-expected.yaml index 1d82b5da3..a179f55c8 100644 --- a/integration-tests/goss/centos7/goss-expected.yaml +++ b/integration-tests/goss/centos7/goss-expected.yaml @@ -2,7 +2,6 @@ file: /etc/passwd: exists: true mode: "0644" - size: 810 owner: root group: root filetype: file diff --git a/integration-tests/goss/goss-shared.yaml b/integration-tests/goss/goss-shared.yaml index c3d1516ba..ed75773e2 100644 --- a/integration-tests/goss/goss-shared.yaml +++ b/integration-tests/goss/goss-shared.yaml @@ -252,8 +252,6 @@ matching: baz: bing matches: and: - - have-key-with-value: - foo: bar - have-key: baz semver: content: diff --git a/integration-tests/goss/trusty/goss-expected.yaml b/integration-tests/goss/trusty/goss-expected.yaml index 6310a6656..fbc6b3d7c 100644 --- a/integration-tests/goss/trusty/goss-expected.yaml +++ b/integration-tests/goss/trusty/goss-expected.yaml @@ -2,7 +2,6 @@ file: /etc/passwd: exists: true mode: "0644" - size: 1006 owner: root group: root filetype: file diff --git a/integration-tests/goss/wheezy/goss-expected.yaml b/integration-tests/goss/wheezy/goss-expected.yaml index d500dff16..d93a2b67f 100644 --- a/integration-tests/goss/wheezy/goss-expected.yaml +++ b/integration-tests/goss/wheezy/goss-expected.yaml @@ -2,7 +2,6 @@ file: /etc/passwd: exists: true mode: "0644" - size: 812 owner: root group: root filetype: file diff --git a/integration-tests/goss/windows/tests/command.goss.yaml b/integration-tests/goss/windows/tests/command.goss.yaml index 26a48dcf1..e5955ed2f 100644 --- a/integration-tests/goss/windows/tests/command.goss.yaml +++ b/integration-tests/goss/windows/tests/command.goss.yaml @@ -11,14 +11,14 @@ command: exec: powershell -noprofile -noninteractive -command (get-itemproperty -path 'HKLM:/SYSTEM/CurrentControlSet/Control/Lsa/').restrictanonymous exit-status: 0 stdout: - - 0 + - "0" stderr: [] timeout: 10000 wrap a powershell with quotes - expect 0 because travis does not restrict anonymous logins: exec: powershell -noprofile -noninteractive -command "(get-itemproperty -path 'HKLM:/SYSTEM/CurrentControlSet/Control/Lsa/').restrictanonymous" exit-status: 0 stdout: - - 0 + - "0" stderr: [] timeout: 10000 powershell with quotes: diff --git a/matchers/and.go b/matchers/and.go new file mode 100644 index 000000000..463a6dbc1 --- /dev/null +++ b/matchers/and.go @@ -0,0 +1,50 @@ +package matchers + +import ( + "encoding/json" + "fmt" +) + +type AndMatcher struct { + fakeOmegaMatcher + Matchers []GossMatcher + + // state + firstFailedMatcher GossMatcher +} + +func And(ms ...GossMatcher) GossMatcher { + return &AndMatcher{Matchers: ms} +} + +func (m *AndMatcher) Match(actual interface{}) (success bool, err error) { + m.firstFailedMatcher = nil + for _, matcher := range m.Matchers { + success, err := matcher.Match(actual) + if !success || err != nil { + m.firstFailedMatcher = matcher + return false, err + } + } + return true, nil +} + +func (m *AndMatcher) FailureResult(actual interface{}) MatcherResult { + return m.firstFailedMatcher.FailureResult(actual) +} + +func (m *AndMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: fmt.Sprintf("To not satisfy all of these matchers: %s", m.Matchers), + } +} + +func (m *AndMatcher) MarshalJSON() ([]byte, error) { + if len(m.Matchers) == 1 { + return json.Marshal(m.Matchers[0]) + } + j := make(map[string]interface{}) + j["and"] = m.Matchers + return json.Marshal(j) +} diff --git a/matchers/be_numerically_matcher.go b/matchers/be_numerically_matcher.go new file mode 100644 index 000000000..053de26a0 --- /dev/null +++ b/matchers/be_numerically_matcher.go @@ -0,0 +1,50 @@ +package matchers + +import ( + "encoding/json" + "fmt" + + "github.com/onsi/gomega/matchers" +) + +type BeNumericallyMatcher struct { + matchers.BeNumericallyMatcher +} + +func BeNumerically(comparator string, compareTo ...interface{}) GossMatcher { + return &BeNumericallyMatcher{ + matchers.BeNumericallyMatcher{ + Comparator: comparator, + CompareTo: compareTo, + }, + } +} + +func (m *BeNumericallyMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: fmt.Sprintf("to be %s", m.Comparator), + Expected: m.CompareTo[0], + } +} + +func (m *BeNumericallyMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: fmt.Sprintf("not to be %s", m.Comparator), + Expected: m.CompareTo[0], + } +} + +func (m *BeNumericallyMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j[numericSymbolToStr[m.Comparator]] = m.CompareTo[0] + return json.Marshal(j) +} + +var numericSymbolToStr = map[string]string{ + ">": "gt", + ">=": "ge", + "<": "lt", + "<=": "le", +} diff --git a/matchers/consist_of.go b/matchers/consist_of.go new file mode 100644 index 000000000..94a114011 --- /dev/null +++ b/matchers/consist_of.go @@ -0,0 +1,45 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type ConsistOfMatcher struct { + matchers.ConsistOfMatcher +} + +func ConsistOf(elements ...interface{}) GossMatcher { + return &ConsistOfMatcher{ + matchers.ConsistOfMatcher{ + Elements: elements, + }, + } +} + +func (m *ConsistOfMatcher) FailureResult(actual interface{}) MatcherResult { + missingElements := getUnexported(m, "missingElements") + extraElements := getUnexported(m, "extraElements") + return MatcherResult{ + Actual: actual, + Message: "to consist of", + Expected: m.Elements, + MissingElements: missingElements, + ExtraElements: extraElements, + } +} + +func (m *ConsistOfMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to consist of", + Expected: m.Elements, + } +} + +func (m *ConsistOfMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["consist-of"] = m.Elements + return json.Marshal(j) +} diff --git a/matchers/contain_element_matcher.go b/matchers/contain_element_matcher.go new file mode 100644 index 000000000..a3fc7c5c2 --- /dev/null +++ b/matchers/contain_element_matcher.go @@ -0,0 +1,41 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type ContainElementMatcher struct { + matchers.ContainElementMatcher +} + +func ContainElement(element interface{}) GossMatcher { + return &ContainElementMatcher{ + matchers.ContainElementMatcher{ + Element: element, + }, + } +} + +func (m *ContainElementMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to contain element matching", + Expected: m.Element, + } +} + +func (m *ContainElementMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to contain element matching", + Expected: m.Element, + } +} + +func (m *ContainElementMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["contain-element"] = m.Element + return json.Marshal(j) +} diff --git a/matchers/contain_elements_matcher.go b/matchers/contain_elements_matcher.go new file mode 100644 index 000000000..1d478594f --- /dev/null +++ b/matchers/contain_elements_matcher.go @@ -0,0 +1,43 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type ContainElementsMatcher struct { + matchers.ContainElementsMatcher +} + +func ContainElements(elements ...interface{}) GossMatcher { + return &ContainElementsMatcher{ + matchers.ContainElementsMatcher{ + Elements: elements, + }, + } +} +func (m *ContainElementsMatcher) FailureResult(actual interface{}) MatcherResult { + missingElements := getUnexported(m, "missingElements") + return MatcherResult{ + Actual: actual, + Message: "to contain elements", + Expected: m.Elements, + MissingElements: missingElements, + } + +} +func (m *ContainElementsMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to contain elements", + Expected: m.Elements, + } + +} + +func (m *ContainElementsMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["contain-elements"] = m.Elements + return json.Marshal(j) +} diff --git a/matchers/contain_substring_matcher.go b/matchers/contain_substring_matcher.go new file mode 100644 index 000000000..851218d84 --- /dev/null +++ b/matchers/contain_substring_matcher.go @@ -0,0 +1,42 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type ContainSubstringMatcher struct { + matchers.ContainSubstringMatcher +} + +func ContainSubstring(substr string, args ...interface{}) GossMatcher { + return &ContainSubstringMatcher{ + matchers.ContainSubstringMatcher{ + Substr: substr, + Args: args, + }, + } +} + +func (m *ContainSubstringMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to contain substring", + Expected: m.Substr, + } +} + +func (m *ContainSubstringMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to contain substring", + Expected: m.Substr, + } +} + +func (m *ContainSubstringMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["contain-substring"] = m.Substr + return json.Marshal(j) +} diff --git a/matchers/equal_matcher.go b/matchers/equal_matcher.go new file mode 100644 index 000000000..faa2125ea --- /dev/null +++ b/matchers/equal_matcher.go @@ -0,0 +1,39 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type EqualMatcher struct { + matchers.EqualMatcher +} + +func Equal(element interface{}) GossMatcher { + return &EqualMatcher{ + matchers.EqualMatcher{ + Expected: element, + }, + } +} + +func (m *EqualMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to equal", + Expected: m.Expected, + } +} + +func (m *EqualMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to equal", + Expected: m.Expected, + } +} + +func (m *EqualMatcher) MarshalJSON() ([]byte, error) { + return json.Marshal(m.Expected) +} diff --git a/matchers/have_key_matcher.go b/matchers/have_key_matcher.go new file mode 100644 index 000000000..624203c29 --- /dev/null +++ b/matchers/have_key_matcher.go @@ -0,0 +1,41 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type HaveKeyMatcher struct { + matchers.HaveKeyMatcher +} + +func HaveKey(key interface{}) GossMatcher { + return &HaveKeyMatcher{ + matchers.HaveKeyMatcher{ + Key: key, + }, + } +} + +func (m *HaveKeyMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to have key matching", + Expected: m.Key, + } +} + +func (m *HaveKeyMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to have key matching", + Expected: m.Key, + } +} + +func (m *HaveKeyMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["have-key"] = m.Key + return json.Marshal(j) +} diff --git a/matchers/have_len_matcher.go b/matchers/have_len_matcher.go new file mode 100644 index 000000000..f6b1038e2 --- /dev/null +++ b/matchers/have_len_matcher.go @@ -0,0 +1,41 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type HaveLenMatcher struct { + matchers.HaveLenMatcher +} + +func HaveLen(count int) GossMatcher { + return &HaveLenMatcher{ + matchers.HaveLenMatcher{ + Count: count, + }, + } +} + +func (m *HaveLenMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to have length", + Expected: m.Count, + } +} + +func (m *HaveLenMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to have length", + Expected: m.Count, + } +} + +func (m *HaveLenMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["have-len"] = m.Count + return json.Marshal(j) +} diff --git a/matchers/have_patterns.go b/matchers/have_patterns.go new file mode 100644 index 000000000..8546a7842 --- /dev/null +++ b/matchers/have_patterns.go @@ -0,0 +1,254 @@ +package matchers + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "regexp" + "strings" + + "github.com/onsi/gomega/format" +) + +const ( + maxScanTokenSize = 1024 * 1024 +) + +type HavePatternsMatcher struct { + fakeOmegaMatcher + + Elements interface{} + missingElements []string +} + +func HavePatterns(elements interface{}) GossMatcher { + return &HavePatternsMatcher{ + Elements: elements, + } +} + +func (m *HavePatternsMatcher) Match(actual interface{}) (success bool, err error) { + t, ok := m.Elements.([]interface{}) + if !ok { + return false, fmt.Errorf("HavePatterns matcher expects an io.reader. Got:\n%s", format.Object(actual, 1)) + } + elements := make([]string, len(t)) + for i, v := range t { + switch v := v.(type) { + case string: + elements[i] = v + default: + return false, fmt.Errorf("HavePatterns matcher expects patterns to be a string. got: \n%s", format.Object(v, 1)) + } + } + notfound, err := sliceToPatterns(elements) + if err != nil { + return false, err + } + // short circuit + if len(notfound) == 0 { + return true, nil + } + fh, ok := actual.(io.Reader) + if !ok { + return false, fmt.Errorf("Incorrect type") + } + + defer func() { + if rc, ok := fh.(io.ReadCloser); ok { + rc.Close() + } + }() + + scanner := bufio.NewScanner(fh) + scanner.Buffer(nil, maxScanTokenSize) + var found []patternMatcher + for scanner.Scan() { + line := scanner.Text() + + i := 0 + for _, pat := range notfound { + if pat.Match(line) { + // Found it, but wasn't supposed to, don't mark it as found, but remove it from search + if !pat.Inverse() { + found = append(found, pat) + } + continue + } + notfound[i] = pat + i++ + } + notfound = notfound[:i] + if len(notfound) == 0 { + break + } + } + if err := scanner.Err(); err != nil { + return false, err + } + + for _, pat := range notfound { + // Didn't find it, but we didn't want to.. so we mark it as found + // Empty pattern should match even if input to scanner is empty + if pat.Inverse() || pat.Pattern() == "" { + found = append(found, pat) + } + } + + if len(elements) != len(found) { + found := patternsToSlice(found) + m.missingElements = subtractSlice(elements, found) + return false, nil + } + return true, nil +} + +func (m *HavePatternsMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: fmt.Sprintf("object: %T", actual), + Message: "to contain patterns", + Expected: m.Elements, + MissingElements: m.missingElements, + } +} + +func (m *HavePatternsMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: fmt.Sprintf("object: %T", actual), + Message: "not to contain patterns", + Expected: m.Elements, + } +} + +func appendMissingStrings(message string, missingElements []string) string { + if len(missingElements) == 0 { + return message + } + return fmt.Sprintf("%s\nthe missing elements were\n%s", message, + format.Object(missingElements, 1)) +} + +type patternMatcher interface { + Match(string) bool + Pattern() string + Inverse() bool +} + +type stringPattern struct { + pattern string + cleanPattern string + inverse bool +} + +func newStringPattern(str string) *stringPattern { + var inverse bool + if strings.HasPrefix(str, "!") { + inverse = true + } + cleanPattern := strings.TrimLeft(str, "\\/!") + return &stringPattern{ + pattern: str, + cleanPattern: cleanPattern, + inverse: inverse, + } +} + +func (s *stringPattern) Match(str string) bool { + return strings.Contains(str, s.cleanPattern) +} + +func (s *stringPattern) Pattern() string { return s.pattern } +func (s *stringPattern) Inverse() bool { return s.inverse } + +type regexPattern struct { + pattern string + re *regexp.Regexp + inverse bool +} + +func newRegexPattern(str string) (*regexPattern, error) { + var inverse bool + cleanStr := str + if strings.HasPrefix(str, "!") { + inverse = true + cleanStr = cleanStr[1:] + } + trimLeft := []rune{'\\', '/'} + for _, r := range trimLeft { + if rune(cleanStr[0]) == r { + cleanStr = cleanStr[1:] + break + } + } + trimRight := []rune{'/'} + for _, r := range trimRight { + if rune(cleanStr[len(cleanStr)-1]) == r { + cleanStr = cleanStr[:len(cleanStr)-1] + break + } + } + + re, err := regexp.Compile(cleanStr) + + return ®exPattern{ + pattern: str, + re: re, + inverse: inverse, + }, err + +} + +func (re *regexPattern) Match(str string) bool { + return re.re.MatchString(str) +} + +func (re *regexPattern) Pattern() string { return re.pattern } +func (re *regexPattern) Inverse() bool { return re.inverse } + +func sliceToPatterns(slice []string) ([]patternMatcher, error) { + var patterns []patternMatcher + for _, s := range slice { + if (strings.HasPrefix(s, "/") || strings.HasPrefix(s, "!/")) && strings.HasSuffix(s, "/") { + pat, err := newRegexPattern(s) + if err != nil { + return nil, err + } + patterns = append(patterns, pat) + } else { + patterns = append(patterns, newStringPattern(s)) + } + } + return patterns, nil +} + +func patternsToSlice(patterns []patternMatcher) []string { + var slice []string + for _, p := range patterns { + slice = append(slice, p.Pattern()) + } + return slice +} +func subtractSlice(x, y []string) []string { + m := make(map[string]bool) + + for _, y := range y { + m[y] = true + } + + var ret []string + for _, x := range x { + if m[x] { + continue + } + ret = append(ret, x) + } + + return ret +} + +func (matcher *HavePatternsMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["have-patterns"] = matcher.Elements + return json.Marshal(j) +} diff --git a/matchers/have_prefix_matcher.go b/matchers/have_prefix_matcher.go new file mode 100644 index 000000000..a9c32eb84 --- /dev/null +++ b/matchers/have_prefix_matcher.go @@ -0,0 +1,42 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type HavePrefixMatcher struct { + matchers.HavePrefixMatcher +} + +func HavePrefix(prefix string, args ...interface{}) GossMatcher { + return &HavePrefixMatcher{ + matchers.HavePrefixMatcher{ + Prefix: prefix, + Args: args, + }, + } +} + +func (m *HavePrefixMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to have prefix", + Expected: m.Prefix, + } +} + +func (m *HavePrefixMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to have prefix", + Expected: m.Prefix, + } +} + +func (m *HavePrefixMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["have-prefix"] = m.Prefix + return json.Marshal(j) +} diff --git a/matchers/have_suffix_matcher.go b/matchers/have_suffix_matcher.go new file mode 100644 index 000000000..a4d759296 --- /dev/null +++ b/matchers/have_suffix_matcher.go @@ -0,0 +1,42 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type HaveSuffixMatcher struct { + matchers.HaveSuffixMatcher +} + +func HaveSuffix(prefix string, args ...interface{}) GossMatcher { + return &HaveSuffixMatcher{ + matchers.HaveSuffixMatcher{ + Suffix: prefix, + Args: args, + }, + } +} + +func (m *HaveSuffixMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to have suffix", + Expected: m.Suffix, + } +} + +func (m *HaveSuffixMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to have suffix", + Expected: m.Suffix, + } +} + +func (m *HaveSuffixMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["have-suffix"] = m.Suffix + return json.Marshal(j) +} diff --git a/matchers/match_regexp_matcher.go b/matchers/match_regexp_matcher.go new file mode 100644 index 000000000..aba5c463f --- /dev/null +++ b/matchers/match_regexp_matcher.go @@ -0,0 +1,42 @@ +package matchers + +import ( + "encoding/json" + + "github.com/onsi/gomega/matchers" +) + +type MatchRegexpMatcher struct { + matchers.MatchRegexpMatcher +} + +func MatchRegexp(regexp string, args ...interface{}) GossMatcher { + return &MatchRegexpMatcher{ + matchers.MatchRegexpMatcher{ + Regexp: regexp, + Args: args, + }, + } +} + +func (m *MatchRegexpMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to match regular expression", + Expected: m.Regexp, + } +} + +func (m *MatchRegexpMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to match regular expression", + Expected: m.Regexp, + } +} + +func (m *MatchRegexpMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["match-regexp"] = m.Regexp + return json.Marshal(j) +} diff --git a/matchers/matchers.go b/matchers/matchers.go new file mode 100644 index 000000000..1d03d498e --- /dev/null +++ b/matchers/matchers.go @@ -0,0 +1,47 @@ +package matchers + +import ( + "encoding/json" + "reflect" + "unsafe" + + "github.com/onsi/gomega/types" +) + +type GossMatcher interface { + // This is needed due to oMegaMatcher test in some of the GomegaMatcher logic + types.GomegaMatcher + //Match(actual interface{}) (success bool, err error) + FailureResult(actual interface{}) MatcherResult + NegatedFailureResult(actual interface{}) MatcherResult + json.Marshaler +} + +type MatcherResult struct { + Actual interface{} + Message string + Expected interface{} + MissingElements interface{} + ExtraElements interface{} + TransformerChain []Transformer + UntransformedValue interface{} +} + +func getUnexported(i interface{}, field string) interface{} { + rs := reflect.ValueOf(i).Elem() + rf := rs.FieldByName(field) + rf = reflect.NewAt(rf.Type(), unsafe.Pointer(rf.UnsafeAddr())).Elem() + return rf.Interface() +} + +type fakeOmegaMatcher struct{} + +// FailureMessage is a stub to honor omegaMatcher interface +func (m *fakeOmegaMatcher) FailureMessage(_ interface{}) (message string) { + return "" +} + +// NegatedFailureMessage is a stub to honor omegaMatcher interface +func (m *fakeOmegaMatcher) NegatedFailureMessage(_ interface{}) (message string) { + return "" +} diff --git a/matchers/not.go b/matchers/not.go new file mode 100644 index 000000000..c27373d59 --- /dev/null +++ b/matchers/not.go @@ -0,0 +1,36 @@ +package matchers + +import ( + "encoding/json" +) + +type NotMatcher struct { + fakeOmegaMatcher + Matcher GossMatcher +} + +func Not(matcher GossMatcher) GossMatcher { + return &NotMatcher{Matcher: matcher} +} + +func (m *NotMatcher) Match(actual interface{}) (bool, error) { + success, err := m.Matcher.Match(actual) + if err != nil { + return false, err + } + return !success, nil +} + +func (m *NotMatcher) FailureResult(actual interface{}) MatcherResult { + return m.Matcher.NegatedFailureResult(actual) +} + +func (m *NotMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return m.Matcher.FailureResult(actual) +} + +func (m *NotMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["not"] = m.Matcher + return json.Marshal(j) +} diff --git a/matchers/or.go b/matchers/or.go new file mode 100644 index 000000000..90ea73d74 --- /dev/null +++ b/matchers/or.go @@ -0,0 +1,52 @@ +package matchers + +import ( + "encoding/json" +) + +type OrMatcher struct { + fakeOmegaMatcher + + Matchers []GossMatcher + + // state + firstSuccessfulMatcher GossMatcher +} + +func Or(ms ...GossMatcher) GossMatcher { + return &OrMatcher{Matchers: ms} +} + +func (m *OrMatcher) Match(actual interface{}) (success bool, err error) { + m.firstSuccessfulMatcher = nil + for _, matcher := range m.Matchers { + success, err := matcher.Match(actual) + if err != nil { + return false, err + } + if success { + m.firstSuccessfulMatcher = matcher + return true, nil + } + } + return false, nil +} + +func (m *OrMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "To satisfy at least one of these matchers", + Expected: m.Matchers, + } +} + +func (m *OrMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + firstSuccessfulMatcher := getUnexported(m, "firstSuccessfulMatcher") + return firstSuccessfulMatcher.(GossMatcher).NegatedFailureResult(actual) +} + +func (m *OrMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["or"] = m.Matchers + return json.Marshal(j) +} diff --git a/matchers/semver_constraint.go b/matchers/semver_constraint.go index 614c661c2..acbe0a84c 100644 --- a/matchers/semver_constraint.go +++ b/matchers/semver_constraint.go @@ -1,28 +1,30 @@ package matchers import ( + "bytes" + "encoding/json" "fmt" "reflect" "github.com/blang/semver" "github.com/onsi/gomega/format" - "github.com/onsi/gomega/types" ) -func BeSemverConstraint(constraint interface{}) types.GomegaMatcher { - return &BeSemverConstraintMatcher{ - Constraint: constraint, - } -} - type BeSemverConstraintMatcher struct { + fakeOmegaMatcher + Constraint interface{} } -func (matcher *BeSemverConstraintMatcher) Match(actual interface{}) (success bool, err error) { - constraint, ok := toConstraint(matcher.Constraint) +func BeSemverConstraint(constraint interface{}) GossMatcher { + return &BeSemverConstraintMatcher{ + Constraint: constraint, + } +} +func (m *BeSemverConstraintMatcher) Match(actual interface{}) (success bool, err error) { + constraint, ok := toConstraint(m.Constraint) if !ok { - return false, fmt.Errorf("Expected a valid semver constraint. Got:\n%s", format.Object(matcher.Constraint, 1)) + return false, fmt.Errorf("Expected a valid semver constraint. Got:\n%s", format.Object(m.Constraint, 1)) } actualSlice, ok := toVersions(actual) @@ -39,12 +41,20 @@ func (matcher *BeSemverConstraintMatcher) Match(actual interface{}) (success boo return true, nil } -func (matcher *BeSemverConstraintMatcher) FailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("to be %s", matcher.Constraint)) +func (m *BeSemverConstraintMatcher) FailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "to satisfy constraint", + Expected: m.Constraint, + } } -func (matcher *BeSemverConstraintMatcher) NegatedFailureMessage(actual interface{}) (message string) { - return format.Message(actual, fmt.Sprintf("not to be %s", matcher.Constraint)) +func (m *BeSemverConstraintMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + return MatcherResult{ + Actual: actual, + Message: "not to satisfy constraint", + Expected: m.Constraint, + } } func toConstraint(in interface{}) (semver.Range, bool) { @@ -102,3 +112,17 @@ func toVersions(in interface{}) ([]*semver.Version, bool) { return out, len(out) > 0 } + +func (m *BeSemverConstraintMatcher) MarshalJSON() ([]byte, error) { + j := make(map[string]interface{}) + j["semver-constraint"] = m.Constraint + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + err := encoder.Encode(j) + if err != nil { + return nil, nil + } + b := buffer.Bytes() + return b, nil +} diff --git a/matchers/semver_constraint_test.go b/matchers/semver_constraint_test.go index fc6f7f752..a2a761ecb 100644 --- a/matchers/semver_constraint_test.go +++ b/matchers/semver_constraint_test.go @@ -7,7 +7,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/blang/semver" - "github.com/onsi/gomega/types" ) func TestBeSemverConstraint(t *testing.T) { @@ -17,7 +16,7 @@ func TestBeSemverConstraint(t *testing.T) { tests := []struct { name string args args - want types.GomegaMatcher + want GossMatcher }{ { name: "sanity", @@ -42,22 +41,20 @@ func TestBeSemverConstraintMatcher_FailureMessage(t *testing.T) { actual interface{} } tests := []struct { - name string - fields fields - args args - wantMessage string + name string + fields fields + args args + wantResult MatcherResult }{ { - name: "string", - fields: fields{Constraint: "> 1.1.0"}, - args: args{actual: "1.0.0"}, - wantMessage: "Expected\n : 1.0.0\nto be > 1.1.0", - }, - { - name: "slice_string", - fields: fields{Constraint: "> 1.1.0"}, - args: args{actual: []string{"1.0.0"}}, - wantMessage: "Expected\n <[]string | len:1, cap:1>: [\"1.0.0\"]\nto be > 1.1.0", + name: "string", + fields: fields{Constraint: "> 1.1.0"}, + args: args{actual: "1.0.0"}, + wantResult: MatcherResult{ + Actual: "1.0.0", + Message: "to satisfy constraint", + Expected: "> 1.1.0", + }, }, } for _, tt := range tests { @@ -65,8 +62,8 @@ func TestBeSemverConstraintMatcher_FailureMessage(t *testing.T) { matcher := &BeSemverConstraintMatcher{ Constraint: tt.fields.Constraint, } - gotMessage := matcher.FailureMessage(tt.args.actual) - assert.Equal(t, tt.wantMessage, gotMessage) + gotResult := matcher.FailureResult(tt.args.actual) + assert.Equal(t, tt.wantResult, gotResult) }) } } @@ -171,22 +168,20 @@ func TestBeSemverConstraintMatcher_NegatedFailureMessage(t *testing.T) { actual interface{} } tests := []struct { - name string - fields fields - args args - wantMessage string + name string + fields fields + args args + wantResult MatcherResult }{ { - name: "string", - fields: fields{Constraint: "> 1.1.0"}, - args: args{actual: "1.0.0"}, - wantMessage: "Expected\n : 1.0.0\nnot to be > 1.1.0", - }, - { - name: "slice_string", - fields: fields{Constraint: "> 1.1.0"}, - args: args{actual: []string{"1.0.0"}}, - wantMessage: "Expected\n <[]string | len:1, cap:1>: [\"1.0.0\"]\nnot to be > 1.1.0", + name: "string", + fields: fields{Constraint: "> 1.1.0"}, + args: args{actual: "1.0.0"}, + wantResult: MatcherResult{ + Actual: "1.0.0", + Message: "not to satisfy constraint", + Expected: "> 1.1.0", + }, }, } for _, tt := range tests { @@ -195,8 +190,8 @@ func TestBeSemverConstraintMatcher_NegatedFailureMessage(t *testing.T) { Constraint: tt.fields.Constraint, } - gotMessage := matcher.NegatedFailureMessage(tt.args.actual) - assert.Equal(t, tt.wantMessage, gotMessage) + gotResult := matcher.NegatedFailureResult(tt.args.actual) + assert.Equal(t, tt.wantResult, gotResult) }) } } diff --git a/matchers/type_conversion.go b/matchers/type_conversion.go new file mode 100644 index 000000000..c0838da6c --- /dev/null +++ b/matchers/type_conversion.go @@ -0,0 +1,138 @@ +package matchers + +import ( + "encoding/json" + "fmt" + "io" + "io/ioutil" + "strconv" + "strings" + + "github.com/onsi/gomega/format" + "github.com/tidwall/gjson" +) + +type Transformer interface { + Transform(interface{}) (interface{}, error) +} + +type ToNumeric struct{} + +func (t ToNumeric) Transform(e interface{}) (interface{}, error) { + switch v := e.(type) { + case float64, int: + return v, nil + case string: + return strconv.ParseFloat(strings.TrimSpace(v), 64) + case []string: + i, err := ToString{}.Transform(v) + if err != nil { + return 0, err + } + s := i.(string) + return strconv.ParseFloat(strings.TrimSpace(s), 64) + default: + return 0, fmt.Errorf("Expected numeric, Got:%s", format.Object(e, 1)) + + } +} +func (t ToNumeric) MarshalJSON() ([]byte, error) { + j := map[string]interface{}{ + "to-numeric": map[string]string{}, + } + return json.Marshal(j) +} + +type ToString struct{} + +func (t ToString) Transform(e interface{}) (interface{}, error) { + switch v := e.(type) { + case []string: + return strings.Join(v, "\n"), nil + default: + return fmt.Sprintf("%v", v), nil + } +} + +func (t ToString) MarshalJSON() ([]byte, error) { + j := map[string]interface{}{ + "to-string": map[string]string{}, + } + return json.Marshal(j) +} + +type ToArray struct{} + +func (t ToArray) Transform(i interface{}) (interface{}, error) { + switch v := i.(type) { + case string: + return strings.Split(v, "\n"), nil + default: + return i, nil + } +} +func (matcher ToArray) MarshalJSON() ([]byte, error) { + j := map[string]interface{}{ + "to-array": map[string]string{}, + } + return json.Marshal(j) +} + +type ReaderToStrings struct{} + +func (t ReaderToStrings) Transform(i interface{}) (interface{}, error) { + r, ok := i.(io.Reader) + if !ok { + return nil, fmt.Errorf("Expected io.reader, Got:%s", format.Object(i, 1)) + } + var lines []string + i, err := ReaderToString{}.Transform(r) + if err != nil { + return lines, err + } + s := i.(string) + return strings.Split(s, "\n"), nil +} + +type ReaderToString struct{} + +func (t ReaderToString) Transform(i interface{}) (interface{}, error) { + r, ok := i.(io.Reader) + if !ok { + return nil, fmt.Errorf("Expected io.reader, Got:%s", format.Object(i, 1)) + } + + b, err := ioutil.ReadAll(r) + if err != nil { + return "", err + } + return string(b), nil +} + +type Gjson struct { + Path string +} + +func (g Gjson) Transform(i interface{}) (interface{}, error) { + s, ok := i.(string) + if !ok { + return nil, fmt.Errorf("Expected string, Got:%s", format.Object(i, 1)) + } + if !gjson.Valid(s) { + return nil, fmt.Errorf("Invalid json") + } + r := gjson.Get(s, g.Path) + if !r.Exists() { + return nil, fmt.Errorf("Path not found: %s", g.Path) + } + + return r.Value(), nil +} +func (g Gjson) MarshalJSON() ([]byte, error) { + j := map[string]interface{}{ + "gjson": map[string]string{ + "Path": g.Path, + }, + } + return json.Marshal(j) +} diff --git a/matchers/with_safe_transform.go b/matchers/with_safe_transform.go new file mode 100644 index 000000000..0629c1417 --- /dev/null +++ b/matchers/with_safe_transform.go @@ -0,0 +1,82 @@ +package matchers + +import ( + "encoding/json" + "fmt" + "reflect" +) + +type WithSafeTransformMatcher struct { + fakeOmegaMatcher + + // input + Transform Transformer // must be a function of one parameter that returns one value + Matcher GossMatcher + + // state + transformedValue interface{} + wasTransformed bool +} + +func WithSafeTransform(transform Transformer, matcher GossMatcher) GossMatcher { + + return &WithSafeTransformMatcher{ + Transform: transform, + Matcher: matcher, + } +} + +func (m *WithSafeTransformMatcher) Match(actual interface{}) (bool, error) { + var err error + //log.Printf("%#v: input: %v", m.Transform, actual) + m.transformedValue, err = m.Transform.Transform(actual) + if !reflect.DeepEqual(actual, m.transformedValue) { + m.wasTransformed = true + } + if err != nil { + return false, fmt.Errorf("%#v: %s", m.Transform, err) + } + //log.Printf("%#v: output: %v", m.Transform, m.transformedValue) + return m.Matcher.Match(m.transformedValue) +} + +func (m *WithSafeTransformMatcher) FailureResult(actual interface{}) MatcherResult { + tchain, matcher, tvalue := m.getTransformerChainAndMatcher() + result := matcher.FailureResult(tvalue) + result.TransformerChain = tchain + result.UntransformedValue = actual + return result +} +func (m *WithSafeTransformMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + tchain, matcher, tvalue := m.getTransformerChainAndMatcher() + result := matcher.NegatedFailureResult(tvalue) + result.TransformerChain = tchain + result.UntransformedValue = actual + return result +} + +func (m *WithSafeTransformMatcher) getTransformerChainAndMatcher() (tchain []Transformer, matcher GossMatcher, tvalue interface{}) { + matcher = m + tvalue = m.transformedValue +L: + for { + switch v := matcher.(type) { + case *WithSafeTransformMatcher: + matcher = v.Matcher + tvalue = v.transformedValue + if v.wasTransformed { + tchain = append(tchain, v.Transform) + } + default: + break L + + } + } + return tchain, matcher, tvalue + +} + +func (m *WithSafeTransformMatcher) MarshalJSON() ([]byte, error) { + _, matcher, _ := m.getTransformerChainAndMatcher() + return json.Marshal(matcher) +} diff --git a/outputs/bench.go b/outputs/bench.go new file mode 100644 index 000000000..1777cd5e3 --- /dev/null +++ b/outputs/bench.go @@ -0,0 +1,39 @@ +package outputs + +import ( + "fmt" + "io" + "time" + + "github.com/aelsabbahy/goss/resource" + "github.com/aelsabbahy/goss/util" +) + +type Bench struct{} + +func (r Bench) ValidOptions() []*formatOption { + return []*formatOption{} +} + +func (r Bench) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + var testCount, skipped, failed int + for resultGroup := range results { + for _, testResult := range resultGroup { + fmt.Fprintf(w, "%v %s\n", testResult.Duration, humanizeResult(testResult, true, includeRaw)) + switch testResult.Result { + case resource.SKIP: + skipped++ + case resource.FAIL: + failed++ + } + testCount++ + } + } + + fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) + if failed > 0 { + return 1 + } + return 0 +} diff --git a/outputs/documentation.go b/outputs/documentation.go index 8b01776fa..8a3e550a5 100644 --- a/outputs/documentation.go +++ b/outputs/documentation.go @@ -17,6 +17,7 @@ func (r Documentation) ValidOptions() []*formatOption { func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) testCount := 0 var failedOrSkipped [][]resource.TestResult @@ -31,13 +32,13 @@ func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, for _, testResult := range resultGroup { switch testResult.Result { case resource.SUCCESS: - fmt.Fprintln(w, humanizeResult(testResult)) + fmt.Fprintln(w, humanizeResult(testResult, false, includeRaw)) case resource.SKIP: - fmt.Fprintln(w, humanizeResult(testResult)) + fmt.Fprintln(w, humanizeResult(testResult, false, includeRaw)) failedOrSkippedGroup = append(failedOrSkippedGroup, testResult) skipped++ case resource.FAIL: - fmt.Fprintln(w, humanizeResult(testResult)) + fmt.Fprintln(w, humanizeResult(testResult, false, includeRaw)) failedOrSkippedGroup = append(failedOrSkippedGroup, testResult) failed++ } @@ -49,7 +50,7 @@ func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, } fmt.Fprint(w, "\n\n") - fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped)) + fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped, includeRaw)) fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) if failed > 0 { diff --git a/outputs/json.go b/outputs/json.go index f8568bf48..d1fb4438a 100644 --- a/outputs/json.go +++ b/outputs/json.go @@ -24,6 +24,7 @@ func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, var pretty bool pretty = util.IsValueInList(foPretty, outConfig.FormatOptions) + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) color.NoColor = true testCount := 0 @@ -31,11 +32,12 @@ func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, var resultsOut []map[string]interface{} for resultGroup := range results { for _, testResult := range resultGroup { - if !testResult.Successful { + if testResult.Result == resource.FAIL { failed++ } m := struct2map(testResult) - m["summary-line"] = humanizeResult(testResult) + m["summary-line"] = humanizeResult(testResult, false, includeRaw) + m["summary-line-compact"] = humanizeResult(testResult, true, includeRaw) m["duration"] = int64(m["duration"].(float64)) resultsOut = append(resultsOut, m) testCount++ diff --git a/outputs/json_oneline.go b/outputs/json_oneline.go deleted file mode 100644 index 4c807bac2..000000000 --- a/outputs/json_oneline.go +++ /dev/null @@ -1,59 +0,0 @@ -package outputs - -import ( - "encoding/json" - "fmt" - "io" - "time" - - "github.com/aelsabbahy/goss/resource" - "github.com/aelsabbahy/goss/util" - "github.com/fatih/color" -) - -type JsonOneline struct{} - -func (r JsonOneline) ValidOptions() []*formatOption { - return []*formatOption{} -} - -func (r JsonOneline) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { - - color.NoColor = true - testCount := 0 - failed := 0 - var resultsOut []map[string]interface{} - for resultGroup := range results { - for _, testResult := range resultGroup { - if !testResult.Successful { - failed++ - } - m := struct2map(testResult) - m["summary-line"] = humanizeResult(testResult) - m["duration"] = int64(m["duration"].(float64)) - resultsOut = append(resultsOut, m) - testCount++ - } - } - - summary := make(map[string]interface{}) - duration := time.Since(startTime) - summary["test-count"] = testCount - summary["failed-count"] = failed - summary["total-duration"] = duration - summary["summary-line"] = fmt.Sprintf("Count: %d, Failed: %d, Duration: %.3fs", testCount, failed, duration.Seconds()) - - out := make(map[string]interface{}) - out["results"] = resultsOut - out["summary"] = summary - - j, _ := json.Marshal(out) - fmt.Fprintln(w, string(j)) - - if failed > 0 { - return 1 - } - - return 0 -} diff --git a/outputs/junit.go b/outputs/junit.go index a3c4a048e..44dc4b58e 100644 --- a/outputs/junit.go +++ b/outputs/junit.go @@ -21,6 +21,7 @@ func (r JUnit) ValidOptions() []*formatOption { func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) color.NoColor = true var testCount, failed, skipped int @@ -42,10 +43,10 @@ func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, "time=\"" + duration + "\">\n" if testResult.Result == resource.FAIL { summary[testCount] += "" + - escapeString(humanizeResult2(testResult)) + + escapeString(humanizeResult(testResult, true, includeRaw)) + "\n" summary[testCount] += "" + - escapeString(humanizeResult2(testResult)) + + escapeString(humanizeResult(testResult, true, includeRaw)) + "\n\n" failed++ @@ -55,7 +56,7 @@ func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, skipped++ } summary[testCount] += "" + - escapeString(humanizeResult2(testResult)) + + escapeString(humanizeResult(testResult, true, includeRaw)) + "\n\n" } testCount++ diff --git a/outputs/nagios.go b/outputs/nagios.go index 22e0e7fb3..09ce02f36 100644 --- a/outputs/nagios.go +++ b/outputs/nagios.go @@ -27,6 +27,7 @@ func (r Nagios) Output(w io.Writer, results <-chan []resource.TestResult, var perfdata, verbose bool perfdata = util.IsValueInList(foPerfData, outConfig.FormatOptions) verbose = util.IsValueInList(foVerbose, outConfig.FormatOptions) + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) var summary map[int]string summary = make(map[int]string) @@ -36,7 +37,7 @@ func (r Nagios) Output(w io.Writer, results <-chan []resource.TestResult, switch testResult.Result { case resource.FAIL: if util.IsValueInList(foVerbose, outConfig.FormatOptions) { - summary[failed] = "Fail " + strconv.Itoa(failed+1) + " - " + humanizeResult2(testResult) + "\n" + summary[failed] = "Fail " + strconv.Itoa(failed+1) + " - " + humanizeResult(testResult, true, includeRaw) + "\n" } failed++ case resource.SKIP: diff --git a/outputs/outputs.go b/outputs/outputs.go index 55cc87680..139410111 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -1,16 +1,22 @@ package outputs import ( + "bytes" + "encoding/json" "fmt" "io" + "reflect" + "regexp" "sort" "strings" "sync" "time" + "unicode" "github.com/aelsabbahy/goss/resource" "github.com/aelsabbahy/goss/util" "github.com/fatih/color" + "github.com/icza/dyno" ) type formatOption struct { @@ -26,7 +32,6 @@ var ( outputersMu sync.Mutex outputers = map[string]Outputer{ "documentation": &Documentation{}, - "json_oneline": &JsonOneline{}, "json": &Json{}, "junit": &JUnit{}, "nagios": &Nagios{}, @@ -34,69 +39,112 @@ var ( "structured": &Structured{}, "tap": &Tap{}, "silent": &Silent{}, + "bench": &Bench{}, } - foPerfData = "perfdata" - foVerbose = "verbose" - foPretty = "pretty" + foPerfData = "perfdata" + foVerbose = "verbose" + foPretty = "pretty" + foIncludeRaw = "include_raw" ) var green = color.New(color.FgGreen).SprintfFunc() var red = color.New(color.FgRed).SprintfFunc() var yellow = color.New(color.FgYellow).SprintfFunc() +var multiple_space = regexp.MustCompile(`\s+`) -func humanizeResult(r resource.TestResult) string { - if r.Err != nil { - return red("%s: %s: Error: %s", r.ResourceId, r.Property, r.Err) +func humanizeResult(r resource.TestResult, compact bool, includeRaw bool) string { + sep := "\n" + if compact { + sep = " " } switch r.Result { case resource.SUCCESS: - return green("%s: %s: %s: matches expectation: %s", r.ResourceType, r.ResourceId, r.Property, r.Expected) + return green("%s: %s: %s: %s: %s", r.ResourceType, r.ResourceId, r.Property, r.MatcherResult.Message, prettyPrint(r.MatcherResult.Expected, false)) + case resource.FAIL: + matcherResult := prettyPrintTestResult(r, compact, includeRaw) + return red("%s: %s: %s:%s%s", r.ResourceType, r.ResourceId, r.Property, sep, matcherResult) case resource.SKIP: return yellow("%s: %s: %s: skipped", r.ResourceType, r.ResourceId, r.Property) - case resource.FAIL: - if r.Human != "" { - return red("%s: %s: %s:\n%s", r.ResourceType, r.ResourceId, r.Property, r.Human) - } - return humanizeResult2(r) default: panic(fmt.Sprintf("Unexpected Result Code: %v\n", r.Result)) } } -func humanizeResult2(r resource.TestResult) string { - if r.Err != nil { - return red("%s: %s: Error: %s", r.ResourceId, r.Property, r.Err) +func prettyPrintTestResult(t resource.TestResult, compact bool, includeRaw bool) string { + sep := "\n" + if compact { + sep = " " } - - switch r.Result { - case resource.SUCCESS: - switch r.TestType { - case resource.Value: - return green("%s: %s: %s: matches expectation: %s", r.ResourceType, r.ResourceId, r.Property, r.Expected) - case resource.Values: - return green("%s: %s: %s: all expectations found: [%s]", r.ResourceType, r.ResourceId, r.Property, strings.Join(r.Expected, ", ")) - case resource.Contains: - return green("%s: %s: %s: all expectations found: [%s]", r.ResourceType, r.ResourceId, r.Property, strings.Join(r.Expected, ", ")) - default: - return red("Unexpected type %d", r.TestType) + m := t.MatcherResult + var ss []string + //var s string + if t.Err != nil { + e := fmt.Sprint(t.Err) + if compact { + e = multiple_space.ReplaceAllString(e, " ") + } else { + e = indentLines(e) } - case resource.FAIL: - switch r.TestType { - case resource.Value: - return red("%s: %s: %s: doesn't match, expect: %s found: %s", r.ResourceType, r.ResourceId, r.Property, r.Expected, r.Found) - case resource.Values: - return red("%s: %s: %s: expectations not found [%s]", r.ResourceType, r.ResourceId, r.Property, strings.Join(subtractSlice(r.Expected, r.Found), ", ")) - case resource.Contains: - return red("%s: %s: %s: patterns not found: [%s]", r.ResourceType, r.ResourceId, r.Property, strings.Join(subtractSlice(r.Expected, r.Found), ", ")) - default: - return red("Unexpected type %d", r.TestType) + ss = append(ss, "Error") + ss = append(ss, e) + } else { + ss = append(ss, "Expected") + ss = append(ss, prettyPrint(m.Actual, !compact)) + ss = append(ss, m.Message) + ss = append(ss, prettyPrint(m.Expected, !compact)) + } + + if reflect.ValueOf(m.MissingElements).IsValid() && !reflect.ValueOf(m.MissingElements).IsNil() { + ss = append(ss, "the missing elements were") + ss = append(ss, prettyPrint(m.MissingElements, !compact)) + } + if reflect.ValueOf(m.ExtraElements).IsValid() && !reflect.ValueOf(m.ExtraElements).IsNil() { + ss = append(ss, "the extra elements were") + ss = append(ss, prettyPrint(m.ExtraElements, !compact)) + } + if len(m.TransformerChain) != 0 { + ss = append(ss, "the transform chain was") + ss = append(ss, prettyPrint(m.TransformerChain, !compact)) + if includeRaw { + ss = append(ss, "the raw value was") + ss = append(ss, prettyPrint(m.UntransformedValue, !compact)) } - case resource.SKIP: - return yellow("%s: %s: %s: skipped", r.ResourceType, r.ResourceId, r.Property) - default: - panic(fmt.Sprintf("Unexpected Result Code: %v\n", r.Result)) } + return strings.Join(ss, sep) +} + +func prettyPrint(i interface{}, indent bool) string { + // JSON doesn't like non-string keys + i = dyno.ConvertMapI2MapS(i) + // fixme: error handling + buffer := &bytes.Buffer{} + encoder := json.NewEncoder(buffer) + encoder.SetEscapeHTML(false) + var b []byte + err := encoder.Encode(i) + if err == nil { + b = buffer.Bytes() + } else { + //b = []byte(fmt.Sprint(err)) + b = []byte(fmt.Sprint(i)) + } + b = bytes.TrimRightFunc(b, unicode.IsSpace) + if indent { + return indentLines(string(b)) + } else { + return string(b) + } +} + +// indents a block of text with an indent string +func indentLines(text string) string { + indent := " " + result := "" + for _, j := range strings.Split(strings.TrimRight(text, "\n"), "\n") { + result += indent + j + "\n" + } + return result[:len(result)-1] } func RegisterOutputer(name string, outputer Outputer) { @@ -160,24 +208,6 @@ func GetOutputer(name string) (Outputer, error) { return outputers[name], nil } -func subtractSlice(x, y []string) []string { - m := make(map[string]bool) - - for _, y := range y { - m[y] = true - } - - var ret []string - for _, x := range x { - if m[x] { - continue - } - ret = append(ret, x) - } - - return ret -} - func header(t resource.TestResult) string { var out string if t.Title != "" { @@ -209,7 +239,7 @@ func summary(startTime time.Time, count, failed, skipped int) string { return s } -func failedOrSkippedSummary(failedOrSkipped [][]resource.TestResult) string { +func failedOrSkippedSummary(failedOrSkipped [][]resource.TestResult, includeRaw bool) string { var s string if len(failedOrSkipped) > 0 { s += fmt.Sprint("Failures/Skipped:\n\n") @@ -220,7 +250,7 @@ func failedOrSkippedSummary(failedOrSkipped [][]resource.TestResult) string { s += fmt.Sprint(header) } for _, testResult := range failedGroup { - s += fmt.Sprintln(humanizeResult(testResult)) + s += fmt.Sprintln(humanizeResult(testResult, false, includeRaw)) } s += fmt.Sprint("\n") } diff --git a/outputs/rspecish.go b/outputs/rspecish.go index e7f75f216..355bf48c9 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -44,7 +44,9 @@ func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, } fmt.Fprint(w, "\n\n") - fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped)) + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + + fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped, includeRaw)) fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) if failed > 0 { diff --git a/outputs/structured.go b/outputs/structured.go index 60003f8e8..dad49c70e 100644 --- a/outputs/structured.go +++ b/outputs/structured.go @@ -22,7 +22,8 @@ func (r Structured) ValidOptions() []*formatOption { // StructuredTestResult is an individual test result with additional human friendly summary type StructuredTestResult struct { resource.TestResult - SummaryLine string `json:"summary-line"` + SummaryLine string `json:"summary-line"` + SummaryLineCompact string `json:"summary-line-compact"` } // StructureTestSummary holds summary information about a test run @@ -46,6 +47,7 @@ func (s *StructureTestSummary) String() string { // Output processes output from tests into StructuredOutput written to w as a string func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) result := &StructuredOutput{ Results: []StructuredTestResult{}, Summary: StructureTestSummary{}, @@ -54,11 +56,12 @@ func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, st for resultGroup := range results { for _, testResult := range resultGroup { r := StructuredTestResult{ - TestResult: testResult, - SummaryLine: humanizeResult(testResult), + TestResult: testResult, + SummaryLine: humanizeResult(testResult, false, includeRaw), + SummaryLineCompact: humanizeResult(testResult, true, includeRaw), } - if !testResult.Successful { + if testResult.Result == resource.FAIL { result.Summary.Failed++ } diff --git a/outputs/tap.go b/outputs/tap.go index 43ee080f4..7530e87f4 100644 --- a/outputs/tap.go +++ b/outputs/tap.go @@ -18,6 +18,7 @@ func (r Tap) ValidOptions() []*formatOption { func (r Tap) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) testCount := 0 failed := 0 @@ -29,12 +30,12 @@ func (r Tap) Output(w io.Writer, results <-chan []resource.TestResult, for _, testResult := range resultGroup { switch testResult.Result { case resource.SUCCESS: - summary[testCount] = "ok " + strconv.Itoa(testCount+1) + " - " + humanizeResult2(testResult) + "\n" + summary[testCount] = "ok " + strconv.Itoa(testCount+1) + " - " + humanizeResult(testResult, true, includeRaw) + "\n" case resource.FAIL: - summary[testCount] = "not ok " + strconv.Itoa(testCount+1) + " - " + humanizeResult2(testResult) + "\n" + summary[testCount] = "not ok " + strconv.Itoa(testCount+1) + " - " + humanizeResult(testResult, true, includeRaw) + "\n" failed++ case resource.SKIP: - summary[testCount] = "ok " + strconv.Itoa(testCount+1) + " - # SKIP " + humanizeResult2(testResult) + "\n" + summary[testCount] = "ok " + strconv.Itoa(testCount+1) + " - # SKIP " + humanizeResult(testResult, true, includeRaw) + "\n" default: panic(fmt.Sprintf("Unexpected Result Code: %v\n", testResult.Result)) } diff --git a/resource/command.go b/resource/command.go index c65f17a24..a667dfae0 100644 --- a/resource/command.go +++ b/resource/command.go @@ -12,15 +12,15 @@ import ( ) type Command struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Command string `json:"-" yaml:"-"` - Exec string `json:"exec,omitempty" yaml:"exec,omitempty"` - ExitStatus matcher `json:"exit-status" yaml:"exit-status"` - Stdout []string `json:"stdout" yaml:"stdout"` - Stderr []string `json:"stderr" yaml:"stderr"` - Timeout int `json:"timeout" yaml:"timeout"` - Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + Command string `json:"-" yaml:"-"` + Exec string `json:"exec,omitempty" yaml:"exec,omitempty"` + ExitStatus matcher `json:"exit-status" yaml:"exit-status"` + Stdout matcher `json:"stdout" yaml:"stdout"` + Stderr matcher `json:"stderr" yaml:"stderr"` + Timeout int `json:"timeout" yaml:"timeout"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } func (c *Command) ID() string { return c.Command } @@ -49,11 +49,11 @@ func (c *Command) Validate(sys *system.System) []TestResult { cExitStatus := deprecateAtoI(c.ExitStatus, fmt.Sprintf("%s: command.exit-status", c.Command)) results = append(results, ValidateValue(c, "exit-status", cExitStatus, sysCommand.ExitStatus, skip)) - if len(c.Stdout) > 0 { - results = append(results, ValidateContains(c, "stdout", c.Stdout, sysCommand.Stdout, skip)) + if isSet(c.Stdout) { + results = append(results, ValidateValue(c, "stdout", c.Stdout, sysCommand.Stdout, skip)) } - if len(c.Stderr) > 0 { - results = append(results, ValidateContains(c, "stderr", c.Stderr, sysCommand.Stderr, skip)) + if isSet(c.Stderr) { + results = append(results, ValidateValue(c, "stderr", c.Stderr, sysCommand.Stderr, skip)) } return results } diff --git a/resource/file.go b/resource/file.go index 92d4685dd..eef5c304a 100644 --- a/resource/file.go +++ b/resource/file.go @@ -6,21 +6,21 @@ import ( ) type File struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Path string `json:"-" yaml:"-"` - Exists matcher `json:"exists" yaml:"exists"` - Mode matcher `json:"mode,omitempty" yaml:"mode,omitempty"` - Size matcher `json:"size,omitempty" yaml:"size,omitempty"` - Owner matcher `json:"owner,omitempty" yaml:"owner,omitempty"` - Group matcher `json:"group,omitempty" yaml:"group,omitempty"` - LinkedTo matcher `json:"linked-to,omitempty" yaml:"linked-to,omitempty"` - Filetype matcher `json:"filetype,omitempty" yaml:"filetype,omitempty"` - Contains []string `json:"contains" yaml:"contains"` - Md5 matcher `json:"md5,omitempty" yaml:"md5,omitempty"` - Sha256 matcher `json:"sha256,omitempty" yaml:"sha256,omitempty"` - Sha512 matcher `json:"sha512,omitempty" yaml:"sha512,omitempty"` - Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + Path string `json:"-" yaml:"-"` + Exists matcher `json:"exists" yaml:"exists"` + Mode matcher `json:"mode,omitempty" yaml:"mode,omitempty"` + Size matcher `json:"size,omitempty" yaml:"size,omitempty"` + Owner matcher `json:"owner,omitempty" yaml:"owner,omitempty"` + Group matcher `json:"group,omitempty" yaml:"group,omitempty"` + LinkedTo matcher `json:"linked-to,omitempty" yaml:"linked-to,omitempty"` + Filetype matcher `json:"filetype,omitempty" yaml:"filetype,omitempty"` + Contains matcher `json:"contains" yaml:"contains"` + Md5 matcher `json:"md5,omitempty" yaml:"md5,omitempty"` + Sha256 matcher `json:"sha256,omitempty" yaml:"sha256,omitempty"` + Sha512 matcher `json:"sha512,omitempty" yaml:"sha512,omitempty"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } func (f *File) ID() string { return f.Path } @@ -57,8 +57,8 @@ func (f *File) Validate(sys *system.System) []TestResult { if f.Filetype != nil { results = append(results, ValidateValue(f, "filetype", f.Filetype, sysFile.Filetype, skip)) } - if len(f.Contains) > 0 { - results = append(results, ValidateContains(f, "contains", f.Contains, sysFile.Contains, skip)) + if isSet(f.Contains) { + results = append(results, ValidateValue(f, "contains", f.Contains, sysFile.Contains, skip)) } if f.Size != nil { results = append(results, ValidateValue(f, "size", f.Size, sysFile.Size, skip)) @@ -111,10 +111,5 @@ func NewFile(sysFile system.File, config util.Config) (*File, error) { f.Filetype = filetype } } - if !contains(config.IgnoreList, "size") { - if size, err := sysFile.Size(); err == nil { - f.Size = size - } - } return f, nil } diff --git a/resource/gomega.go b/resource/gomega.go index 0ea454129..6ace6cf4a 100644 --- a/resource/gomega.go +++ b/resource/gomega.go @@ -2,28 +2,28 @@ package resource import ( "fmt" - "sort" "github.com/aelsabbahy/goss/matchers" - - "github.com/onsi/gomega" - "github.com/onsi/gomega/types" ) -func matcherToGomegaMatcher(matcher interface{}) (types.GomegaMatcher, error) { +func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { switch x := matcher.(type) { - case string, int, bool, float64: - return gomega.Equal(x), nil + case string: + return matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal(x)), nil + case float64, int: + return matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("==", x)), nil + case bool: + return matchers.Equal(x), nil case []interface{}: - var matchers []types.GomegaMatcher - for _, valueI := range x { - if subMatcher, ok := valueI.(types.GomegaMatcher); ok { - matchers = append(matchers, subMatcher) - } else { - matchers = append(matchers, gomega.ContainElement(valueI)) - } + subMatchers, err := sliceToGomega(x) + if err != nil { + return nil, err } - return gomega.And(matchers...), nil + var interfaceSlice []interface{} + for _, d := range subMatchers { + interfaceSlice = append(interfaceSlice, d) + } + return matchers.ContainElements(interfaceSlice...), nil } matcher = sanitizeExpectedValue(matcher) if matcher == nil { @@ -39,44 +39,46 @@ func matcherToGomegaMatcher(matcher interface{}) (types.GomegaMatcher, error) { break } switch matchType { + case "equal": + return matchers.Equal(value), nil case "have-prefix": - return gomega.HavePrefix(value.(string)), nil + return matchers.WithSafeTransform(matchers.ToString{}, matchers.HavePrefix(value.(string))), nil case "have-suffix": - return gomega.HaveSuffix(value.(string)), nil + return matchers.WithSafeTransform(matchers.ToString{}, matchers.HaveSuffix(value.(string))), nil case "match-regexp": - return gomega.MatchRegexp(value.(string)), nil + return matchers.WithSafeTransform(matchers.ToString{}, matchers.MatchRegexp(value.(string))), nil + case "contain-substring": + return matchers.WithSafeTransform(matchers.ToString{}, matchers.ContainSubstring(value.(string))), nil case "have-len": - value = sanitizeExpectedValue(value) - return gomega.HaveLen(value.(int)), nil - case "have-key-with-value": - subMatchers, err := mapToGomega(value) - if err != nil { - return nil, err - } - for key, val := range subMatchers { - if val == nil { - fmt.Printf("%d is nil", key) - } - } - return gomega.And(subMatchers...), nil + return matchers.HaveLen(int(value.(float64))), nil case "have-key": subMatcher, err := matcherToGomegaMatcher(value) if err != nil { return nil, err } - return gomega.HaveKey(subMatcher), nil + return matchers.HaveKey(subMatcher), nil case "contain-element": subMatcher, err := matcherToGomegaMatcher(value) if err != nil { return nil, err } - return gomega.ContainElement(subMatcher), nil + return matchers.WithSafeTransform(matchers.ToArray{}, matchers.ContainElement(subMatcher)), nil + case "contain-elements": + subMatchers, err := sliceToGomega(value) + if err != nil { + return nil, err + } + var interfaceSlice []interface{} + for _, d := range subMatchers { + interfaceSlice = append(interfaceSlice, d) + } + return matchers.WithSafeTransform(matchers.ToArray{}, matchers.ContainElements(interfaceSlice...)), nil case "not": subMatcher, err := matcherToGomegaMatcher(value) if err != nil { return nil, err } - return gomega.Not(subMatcher), nil + return matchers.Not(subMatcher), nil case "consist-of": subMatchers, err := sliceToGomega(value) if err != nil { @@ -86,19 +88,19 @@ func matcherToGomegaMatcher(matcher interface{}) (types.GomegaMatcher, error) { for _, d := range subMatchers { interfaceSlice = append(interfaceSlice, d) } - return gomega.ConsistOf(interfaceSlice...), nil + return matchers.ConsistOf(interfaceSlice...), nil case "and": subMatchers, err := sliceToGomega(value) if err != nil { return nil, err } - return gomega.And(subMatchers...), nil + return matchers.And(subMatchers...), nil case "or": subMatchers, err := sliceToGomega(value) if err != nil { return nil, err } - return gomega.Or(subMatchers...), nil + return matchers.Or(subMatchers...), nil case "gt", "ge", "lt", "le": // Golang json escapes '>', '<' symbols, so we use 'gt', 'le' instead comparator := map[string]string{ @@ -107,49 +109,37 @@ func matcherToGomegaMatcher(matcher interface{}) (types.GomegaMatcher, error) { "lt": "<", "le": "<=", }[matchType] - return gomega.BeNumerically(comparator, value), nil + return matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(comparator, value)), nil case "semver-constraint": return matchers.BeSemverConstraint(value.(string)), nil - default: - return nil, fmt.Errorf("Unknown matcher: %s", matchType) - - } -} - -func mapToGomega(value interface{}) (subMatchers []types.GomegaMatcher, err error) { - valueI, ok := value.(map[string]interface{}) - if !ok { - return nil, fmt.Errorf("Matcher expected map, got: %t", value) - } + case "gjson": + var subMatchers []matchers.GossMatcher + valueI, ok := value.(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("Matcher expected map, got: %t", value) + } + for key, val := range valueI { + subMatcher, err := matcherToGomegaMatcher(val) + if err != nil { + return nil, err + } + subMatchers = append(subMatchers, matchers.WithSafeTransform(matchers.Gjson{Path: key}, subMatcher)) - // Get keys - keys := []string{} - for key, _ := range valueI { - keys = append(keys, key) - } - // Iterate through keys in a deterministic way, since ranging over a map - // does not guarantee order - sort.Strings(keys) - for _, key := range keys { - val := valueI[key] - val, err = matcherToGomegaMatcher(val) - if err != nil { - return } + return matchers.And(subMatchers...), nil + default: + return nil, fmt.Errorf("Unknown matcher: %s", matchType) - subMatcher := gomega.HaveKeyWithValue(key, val) - subMatchers = append(subMatchers, subMatcher) } - return } -func sliceToGomega(value interface{}) ([]types.GomegaMatcher, error) { +func sliceToGomega(value interface{}) ([]matchers.GossMatcher, error) { valueI, ok := value.([]interface{}) if !ok { return nil, fmt.Errorf("Matcher expected array, got: %t", value) } - var subMatchers []types.GomegaMatcher + var subMatchers []matchers.GossMatcher for _, v := range valueI { subMatcher, err := matcherToGomegaMatcher(v) if err != nil { @@ -162,8 +152,8 @@ func sliceToGomega(value interface{}) ([]types.GomegaMatcher, error) { // Normalize expectedValue so json and yaml are the same func sanitizeExpectedValue(i interface{}) interface{} { - if e, ok := i.(float64); ok { - return int(e) + if e, ok := i.(int); ok { + return float64(e) } if e, ok := i.(map[interface{}]interface{}); ok { out := make(map[string]interface{}) diff --git a/resource/gomega_test.go b/resource/gomega_test.go index e4dc19ffd..522e010f0 100644 --- a/resource/gomega_test.go +++ b/resource/gomega_test.go @@ -2,15 +2,10 @@ package resource import ( "encoding/json" - "fmt" - "reflect" - "regexp" "testing" "github.com/aelsabbahy/goss/matchers" - - "github.com/onsi/gomega" - "github.com/onsi/gomega/types" + "github.com/stretchr/testify/assert" ) var gomegaTests = []struct { @@ -21,20 +16,22 @@ var gomegaTests = []struct { // Default for simple types { in: `"foo"`, - want: gomega.Equal("foo"), + want: matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal("foo")), }, { in: `1`, - want: gomega.Equal(float64(1)), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("==", float64(1))), }, { in: `true`, - want: gomega.Equal(true), + want: matchers.Equal(true), }, // Default for Array { - in: `["foo", "bar"]`, - want: gomega.And(gomega.ContainElement("foo"), gomega.ContainElement("bar")), + in: `["foo", "bar"]`, + want: matchers.ContainElements( + matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal("foo")), + matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal("bar"))), useNegateTester: true, }, @@ -42,91 +39,103 @@ var gomegaTests = []struct { // Golang json escapes '>', '<' symbols, so we use 'gt', 'le' instead { in: `{"gt": 1}`, - want: gomega.BeNumerically(">", float64(1)), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(">", float64(1))), }, { in: `{"ge": 1}`, - want: gomega.BeNumerically(">=", float64(1)), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(">=", float64(1))), }, { in: `{"lt": 1}`, - want: gomega.BeNumerically("<", float64(1)), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("<", float64(1))), }, { in: `{"le": 1}`, - want: gomega.BeNumerically("<=", float64(1)), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("<=", float64(1))), }, // String { in: `{"have-prefix": "foo"}`, - want: gomega.HavePrefix("foo"), + want: matchers.WithSafeTransform(matchers.ToString{}, matchers.HavePrefix("foo")), }, { in: `{"have-suffix": "foo"}`, - want: gomega.HaveSuffix("foo"), + want: matchers.WithSafeTransform(matchers.ToString{}, matchers.HaveSuffix("foo")), }, // Regex support is based on golangs regex engine https://golang.org/pkg/regexp/syntax/ { in: `{"match-regexp": "foo"}`, - want: gomega.MatchRegexp("foo"), + want: matchers.WithSafeTransform(matchers.ToString{}, matchers.MatchRegexp("foo")), }, // Collection { in: `{"consist-of": ["foo"]}`, - want: gomega.ConsistOf(gomega.Equal("foo")), + want: matchers.ConsistOf(matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal("foo"))), }, { - in: `{"contain-element": "foo"}`, - want: gomega.ContainElement(gomega.Equal("foo")), + in: `{"contain-element": "foo"}`, + want: matchers.WithSafeTransform(matchers.ToArray{}, + matchers.ContainElement( + matchers.WithSafeTransform(matchers.ToString{}, + matchers.Equal("foo")))), }, { in: `{"have-len": 3}`, - want: gomega.HaveLen(3), - }, - { - in: `{"have-key-with-value": { "foo": 1, "bar": "baz" }}`, - // Keys are sorted and then passed to gomega.And so the order - // of the conditions in this `want` is important - want: gomega.And( - gomega.HaveKeyWithValue("bar", gomega.Equal("baz")), - gomega.HaveKeyWithValue("foo", gomega.Equal(1)), - ), - useNegateTester: true, + want: matchers.HaveLen(3), }, { in: `{"have-key": "foo"}`, - want: gomega.HaveKey(gomega.Equal("foo")), + want: matchers.HaveKey(matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal("foo"))), }, // Negation { in: `{"not": "foo"}`, - want: gomega.Not(gomega.Equal("foo")), + want: matchers.Not(matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal("foo"))), }, // Complex logic { - in: `{"and": ["foo", "foo"]}`, - want: gomega.And(gomega.Equal("foo"), gomega.Equal("foo")), + in: `{"and": ["foo", "foo"]}`, + want: matchers.And( + matchers.WithSafeTransform(matchers.ToString{}, + matchers.Equal("foo")), + matchers.WithSafeTransform(matchers.ToString{}, + matchers.Equal("foo")), + ), useNegateTester: true, }, { - in: `{"and": [{"have-prefix": "foo"}, "foo"]}`, - want: gomega.And(gomega.HavePrefix("foo"), gomega.Equal("foo")), + in: `{"and": [{"have-prefix": "foo"}, "foo"]}`, + want: matchers.And( + matchers.WithSafeTransform(matchers.ToString{}, + matchers.HavePrefix("foo")), + matchers.WithSafeTransform(matchers.ToString{}, + matchers.Equal("foo")), + ), useNegateTester: true, }, { - in: `{"not": {"have-prefix": "foo"}}`, - want: gomega.Not(gomega.HavePrefix("foo")), + in: `{"not": {"have-prefix": "foo"}}`, + want: matchers.Not( + matchers.WithSafeTransform(matchers.ToString{}, + matchers.HavePrefix("foo"))), }, { - in: `{"or": ["foo", "foo"]}`, - want: gomega.Or(gomega.Equal("foo"), gomega.Equal("foo")), + in: `{"or": ["foo", "foo"]}`, + want: matchers.Or( + matchers.WithSafeTransform(matchers.ToString{}, + matchers.Equal("foo")), + matchers.WithSafeTransform(matchers.ToString{}, + matchers.Equal("foo"))), }, { - in: `{"not": {"and": [{"have-prefix": "foo"}]}}`, - want: gomega.Not(gomega.And(gomega.HavePrefix("foo"))), + in: `{"not": {"and": [{"have-prefix": "foo"}]}}`, + want: matchers.Not( + matchers.And( + matchers.WithSafeTransform(matchers.ToString{}, + matchers.HavePrefix("foo")))), }, // Semver Constraint @@ -151,34 +160,5 @@ func TestMatcherToGomegaMatcher(t *testing.T) { } func gomegaTestEqual(t *testing.T, got, want interface{}, useNegateTester bool, in string) { - if !gomegaEqual(got, want, useNegateTester) { - t.Errorf("For input '%s': got %T %v, want %T %v", in, got, got, want, want) - } -} -func gomegaEqual(g, w interface{}, negateTester bool) bool { - gotT := reflect.TypeOf(g) - wantT := reflect.TypeOf(w) - got := g.(types.GomegaMatcher) - want := w.(types.GomegaMatcher) - var gotMessage string - var wantMessage string - if negateTester { - gotMessage = got.NegatedFailureMessage("foo") - wantMessage = want.NegatedFailureMessage("foo") - } else { - gotMessage = got.FailureMessage("foo") - wantMessage = want.FailureMessage("foo") - } - gotMessage = sanitizeMatcherText(gotMessage) - wantMessage = sanitizeMatcherText(wantMessage) - fmt.Println("got:", gotMessage) - fmt.Println("want:", wantMessage) - - return gotT == wantT && - gotMessage == wantMessage -} - -func sanitizeMatcherText(s string) string { - r := regexp.MustCompile("[0-9]x[a-z0-9]{10}") - return r.ReplaceAllString(s, "") + assert.Equal(t, got, want) } diff --git a/resource/http.go b/resource/http.go index f95b49f1e..92931977b 100644 --- a/resource/http.go +++ b/resource/http.go @@ -19,8 +19,8 @@ type HTTP struct { Timeout int `json:"timeout" yaml:"timeout"` RequestHeader []string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` RequestBody string `json:"request-bod,omitemptyy" yaml:"request-body,omitempty"` - Headers []string `json:"headers,omitempty" yaml:"headers,omitempty"` - Body []string `json:"body" yaml:"body"` + Headers matcher `json:"headers,omitempty" yaml:"headers,omitempty"` + Body matcher `json:"body" yaml:"body"` Username string `json:"username,omitempty" yaml:"username,omitempty"` Password string `json:"password,omitempty" yaml:"password,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` @@ -63,11 +63,11 @@ func (u *HTTP) Validate(sys *system.System) []TestResult { if shouldSkip(results) { skip = true } - if len(u.Headers) > 0 { - results = append(results, ValidateContains(u, "Headers", u.Headers, sysHTTP.Headers, skip)) + if isSet(u.Headers) { + results = append(results, ValidateGomegaValue(u, "Headers", u.Headers, sysHTTP.Headers, skip)) } - if len(u.Body) > 0 { - results = append(results, ValidateContains(u, "Body", u.Body, sysHTTP.Body, skip)) + if isSet(u.Body) { + results = append(results, ValidateValue(u, "Body", u.Body, sysHTTP.Body, skip)) } return results @@ -80,7 +80,7 @@ func NewHTTP(sysHTTP system.HTTP, config util.Config) (*HTTP, error) { HTTP: http, Status: status, RequestHeader: []string{}, - Headers: []string{}, + Headers: nil, Body: []string{}, AllowInsecure: config.AllowInsecure, NoFollowRedirects: config.NoFollowRedirects, diff --git a/resource/matching.go b/resource/matching.go index 17085f121..5b686cc45 100644 --- a/resource/matching.go +++ b/resource/matching.go @@ -3,6 +3,7 @@ package resource import ( "encoding/json" "fmt" + "io" "reflect" "strings" @@ -11,11 +12,12 @@ import ( ) type Matching struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Content interface{} `json:"content,omitempty" yaml:"content,omitempty"` - Id string `json:"-" yaml:"-"` - Matches matcher `json:"matches" yaml:"matches"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + Content interface{} `json:"content,omitempty" yaml:"content,omitempty"` + AsReader bool `json:"as-reader,omitempty" yaml:"as-reader,omitempty"` + Id string `json:"-" yaml:"-"` + Matches matcher `json:"matches" yaml:"matches"` } type MatchingMap map[string]*Matching @@ -30,9 +32,17 @@ func (r *Matching) GetMeta() meta { return r.Meta } func (a *Matching) Validate(sys *system.System) []TestResult { skip := false - // ValidateValue expects a function - stub := func() (interface{}, error) { - return a.Content, nil + var stub interface{} + if a.AsReader { + // ValidateValue expects a function + stub = func() (io.Reader, error) { + return strings.NewReader(a.Content.(string)), nil + } + } else { + // ValidateValue expects a function + stub = func() (interface{}, error) { + return a.Content, nil + } } var results []TestResult diff --git a/resource/resource.go b/resource/resource.go index 77225cf4a..5b5e33638 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -88,14 +88,17 @@ func shouldSkip(results []TestResult) bool { if len(results) < 1 { return false } - if results[0].Err != nil { - return true - } - if len(results[0].Found) < 1 { - return false - } - if results[0].Found[0] == "false" { + if results[0].Err != nil || results[0].Result != SUCCESS || results[0].MatcherResult.Actual == false { return true } return false } + +func isSet(i interface{}) bool { + switch v := i.(type) { + case []interface{}: + return len(v) > 0 + default: + return i != nil + } +} diff --git a/resource/validate.go b/resource/validate.go index 468d89d2c..ebe068ca9 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -1,16 +1,13 @@ package resource import ( - "bufio" - "encoding/json" "fmt" "io" "reflect" - "regexp" "strings" "time" - "github.com/onsi/gomega/types" + "github.com/aelsabbahy/goss/matchers" ) const ( @@ -20,9 +17,9 @@ const ( ) const ( - SUCCESS = iota - FAIL - SKIP + SUCCESS = "SUCCESS" + FAIL = "FAIL" + SKIP = "SKIP" ) const ( @@ -30,27 +27,26 @@ const ( ) type TestResult struct { - Successful bool `json:"successful" yaml:"successful"` - ResourceId string `json:"resource-id" yaml:"resource-id"` - ResourceType string `json:"resource-type" yaml:"resource-type"` - Title string `json:"title" yaml:"title"` - Meta meta `json:"meta" yaml:"meta"` - TestType int `json:"test-type" yaml:"test-type"` - Result int `json:"result" yaml:"result"` - Property string `json:"property" yaml:"property"` - Err error `json:"err" yaml:"err"` - Expected []string `json:"expected" yaml:"expected"` - Found []string `json:"found" yaml:"found"` - Human string `json:"human" yaml:"human"` - Duration time.Duration `json:"duration" yaml:"duration"` + // Resource data + ResourceId string `json:"resource-id" yaml:"resource-id"` + ResourceType string `json:"resource-type" yaml:"resource-type"` + Property string `json:"property" yaml:"property"` + + // User added info + Title string `json:"title" yaml:"title"` + Meta meta `json:"meta" yaml:"meta"` + + // Result + Result string `json:"result" yaml:"result"` + Err error `json:"err" yaml:"err"` + MatcherResult matchers.MatcherResult `json:"matcher-result" yaml:"matcher-result"` + Duration time.Duration `json:"duration" yaml:"duration"` } -func skipResult(typeS string, testType int, id string, title string, meta meta, property string, startTime time.Time) TestResult { +func skipResult(typeS string, id string, title string, meta meta, property string, startTime time.Time) TestResult { return TestResult{ - Successful: true, Result: SKIP, ResourceType: typeS, - TestType: testType, ResourceId: id, Title: title, Meta: meta, @@ -60,6 +56,25 @@ func skipResult(typeS string, testType int, id string, title string, meta meta, } func ValidateValue(res ResourceRead, property string, expectedValue interface{}, actual interface{}, skip bool) TestResult { + if f, ok := actual.(func() (io.Reader, error)); ok { + if _, ok := expectedValue.([]interface{}); !ok { + actual = func() (string, error) { + v, err := f() + if err != nil { + return "", err + } + i, err := matchers.ReaderToString{}.Transform(v) + if err != nil { + return "", err + } + return i.(string), nil + } + } + } + return ValidateGomegaValue(res, property, expectedValue, actual, skip) +} + +func ValidateGomegaValue(res ResourceRead, property string, expectedValue interface{}, actual interface{}, skip bool) TestResult { id := res.ID() title := res.GetTitle() meta := res.GetMeta() @@ -69,7 +84,6 @@ func ValidateValue(res ResourceRead, property string, expectedValue interface{}, if skip { return skipResult( typeS, - Values, id, title, meta, @@ -79,6 +93,7 @@ func ValidateValue(res ResourceRead, property string, expectedValue interface{}, } var foundValue interface{} + var gomegaMatcher matchers.GossMatcher var err error switch f := actual.(type) { case func() (bool, error): @@ -91,25 +106,23 @@ func ValidateValue(res ResourceRead, property string, expectedValue interface{}, foundValue, err = f() case func() (interface{}, error): foundValue, err = f() + case func() (io.Reader, error): + foundValue, err = f() + gomegaMatcher = matchers.HavePatterns(expectedValue) default: err = fmt.Errorf("Unknown method signature: %t", f) } + foundValue = sanitizeExpectedValue(foundValue) expectedValue = sanitizeExpectedValue(expectedValue) - var gomegaMatcher types.GomegaMatcher var success bool - if err == nil { + if gomegaMatcher == nil && err == nil { gomegaMatcher, err = matcherToGomegaMatcher(expectedValue) } - if err == nil { - success, err = gomegaMatcher.Match(foundValue) - } if err != nil { return TestResult{ - Successful: false, Result: FAIL, ResourceType: typeS, - TestType: Values, ResourceId: id, Title: title, Meta: meta, @@ -119,268 +132,30 @@ func ValidateValue(res ResourceRead, property string, expectedValue interface{}, } } - var failMessage string - var result int - if !success { - failMessage = gomegaMatcher.FailureMessage(foundValue) - result = FAIL - } - - expected, _ := json.Marshal(expectedValue) - found, _ := json.Marshal(foundValue) - - return TestResult{ - Successful: success, - Result: result, - ResourceType: typeS, - TestType: Value, - ResourceId: id, - Title: title, - Meta: meta, - Property: property, - Expected: []string{string(expected)}, - Found: []string{string(found)}, - Human: failMessage, - Err: err, - Duration: time.Now().Sub(startTime), - } -} - -type patternMatcher interface { - Match(string) bool - Pattern() string - Inverse() bool -} - -type stringPattern struct { - pattern string - cleanPattern string - inverse bool -} - -func newStringPattern(str string) *stringPattern { - var inverse bool - if strings.HasPrefix(str, "!") { - inverse = true - } - cleanPattern := strings.TrimLeft(str, "\\/!") - return &stringPattern{ - pattern: str, - cleanPattern: cleanPattern, - inverse: inverse, - } -} - -func (s *stringPattern) Match(str string) bool { - return strings.Contains(str, s.cleanPattern) -} - -func (s *stringPattern) Pattern() string { return s.pattern } -func (s *stringPattern) Inverse() bool { return s.inverse } - -type regexPattern struct { - pattern string - re *regexp.Regexp - inverse bool -} - -func newRegexPattern(str string) (*regexPattern, error) { - var inverse bool - cleanStr := str - if strings.HasPrefix(str, "!") { - inverse = true - cleanStr = cleanStr[1:] - } - trimLeft := []rune{'\\', '/'} - for _, r := range trimLeft { - if rune(cleanStr[0]) == r { - cleanStr = cleanStr[1:] - break - } - } - trimRight := []rune{'/'} - for _, r := range trimRight { - if rune(cleanStr[len(cleanStr)-1]) == r { - cleanStr = cleanStr[:len(cleanStr)-1] - break - } - } - - re, err := regexp.Compile(cleanStr) - - return ®exPattern{ - pattern: str, - re: re, - inverse: inverse, - }, err + success, err = gomegaMatcher.Match(foundValue) -} - -func (re *regexPattern) Match(str string) bool { - return re.re.MatchString(str) -} - -func (re *regexPattern) Pattern() string { return re.pattern } -func (re *regexPattern) Inverse() bool { return re.inverse } - -func sliceToPatterns(slice []string) ([]patternMatcher, error) { - var patterns []patternMatcher - for _, s := range slice { - if (strings.HasPrefix(s, "/") || strings.HasPrefix(s, "!/")) && strings.HasSuffix(s, "/") { - pat, err := newRegexPattern(s) - if err != nil { - return nil, err - } - patterns = append(patterns, pat) - } else { - patterns = append(patterns, newStringPattern(s)) - } - } - return patterns, nil -} - -func patternsToSlice(patterns []patternMatcher) []string { - var slice []string - for _, p := range patterns { - slice = append(slice, p.Pattern()) - } - return slice -} - -func ValidateContains(res ResourceRead, property string, expectedValues []string, method func() (io.Reader, error), skip bool) TestResult { - id := res.ID() - title := res.GetTitle() - meta := res.GetMeta() - typ := reflect.TypeOf(res) - typeS := strings.Split(typ.String(), ".")[1] - startTime := time.Now() - if skip { - return skipResult( - typeS, - Values, - id, - title, - meta, - property, - startTime, - ) - } - var err error - var fh io.Reader - var notfound []patternMatcher - notfound, err = sliceToPatterns(expectedValues) - // short circuit - if len(notfound) == 0 && err == nil { - return TestResult{ - Successful: true, - Result: SUCCESS, - ResourceType: typeS, - TestType: Contains, - ResourceId: id, - Title: title, - Meta: meta, - Property: property, - Expected: expectedValues, - Duration: time.Now().Sub(startTime), - } - } - if err == nil { - fh, err = method() - } - if err != nil { - return TestResult{ - Successful: false, - Result: FAIL, - ResourceType: typeS, - TestType: Contains, - ResourceId: id, - Title: title, - Meta: meta, - Property: property, - Err: err, - Duration: time.Now().Sub(startTime), - } - } - - defer func() { - //Do we need to close the stream? - if rc, ok := fh.(io.ReadCloser); ok { - rc.Close() - } - }() - - scanner := bufio.NewScanner(fh) - scanner.Buffer(nil, maxScanTokenSize) - var found []patternMatcher - for scanner.Scan() { - line := scanner.Text() - - i := 0 - for _, pat := range notfound { - if pat.Match(line) { - // Found it, but wasn't supposed to, don't mark it as found, but remove it from search - if !pat.Inverse() { - found = append(found, pat) - } - continue - } - notfound[i] = pat - i++ - } - notfound = notfound[:i] - if len(notfound) == 0 { - break - } - } - if err := scanner.Err(); err != nil { - return TestResult{ - Successful: false, - Result: FAIL, - ResourceType: typeS, - TestType: Contains, - ResourceId: id, - Title: title, - Meta: meta, - Property: property, - Err: err, - Duration: time.Now().Sub(startTime), - } - } - - for _, pat := range notfound { - // Didn't find it, but we didn't want to.. so we mark it as found - // Empty pattern should match even if input to scanner is empty - if pat.Inverse() || pat.Pattern() == "" { - found = append(found, pat) + var matcherResult matchers.MatcherResult + result := SUCCESS + if success { + matcherResult = matchers.MatcherResult{ + Actual: foundValue, + Message: "matches expectation", + Expected: expectedValue, } + } else { + matcherResult = gomegaMatcher.FailureResult(foundValue) + result = FAIL } - if len(expectedValues) != len(found) { - return TestResult{ - Successful: false, - Result: FAIL, - ResourceType: typeS, - TestType: Contains, - ResourceId: id, - Title: title, - Meta: meta, - Property: property, - Expected: expectedValues, - Found: patternsToSlice(found), - Duration: time.Now().Sub(startTime), - } - } return TestResult{ - Successful: true, - Result: SUCCESS, - ResourceType: typeS, - TestType: Contains, - ResourceId: id, - Title: title, - Meta: meta, - Property: property, - Expected: expectedValues, - Found: patternsToSlice(found), - Duration: time.Now().Sub(startTime), + Result: result, + ResourceType: typeS, + ResourceId: id, + Title: title, + Meta: meta, + Property: property, + MatcherResult: matcherResult, + Err: err, + Duration: time.Now().Sub(startTime), } } diff --git a/resource/validate_test.go b/resource/validate_test.go index efa348f1f..03b0beedc 100644 --- a/resource/validate_test.go +++ b/resource/validate_test.go @@ -20,13 +20,13 @@ func (f *FakeResource) GetMeta() meta { return meta{"foo": "bar"} } var stringTests = []struct { in, in2 interface{} - want bool + want string }{ - {"", "", true}, - {"foo", "foo", true}, - {"foo", "bar", false}, - {"foo", "", false}, - {true, true, true}, + {"", "", SUCCESS}, + {"foo", "foo", SUCCESS}, + {"foo", "bar", FAIL}, + {"foo", "", FAIL}, + {true, true, SUCCESS}, } func TestValidateValue(t *testing.T) { @@ -35,8 +35,8 @@ func TestValidateValue(t *testing.T) { return c.in2, nil } got := ValidateValue(&FakeResource{""}, "", c.in, inFunc, false) - if got.Successful != c.want { - t.Errorf("%+v: got %v, want %v", c, got.Successful, c.want) + if got.Result != c.want { + t.Errorf("%+v: got %v, want %v", c, got.Result, c.want) } } } @@ -47,8 +47,8 @@ func TestValidateValueErr(t *testing.T) { return c.in2, fmt.Errorf("some err") } got := ValidateValue(&FakeResource{""}, "", c.in, inFunc, false) - if got.Successful != false { - t.Errorf("%+v: got %v, want %v", c, got.Successful, false) + if got.Result != FAIL { + t.Errorf("%+v: got %v, want %v", c, got.Result, FAIL) } } } @@ -75,19 +75,19 @@ func BenchmarkValidateValue(b *testing.B) { } var containsTests = []struct { - in []string + in []interface{} in2 string - want bool + want string }{ - {[]string{""}, "", true}, - {[]string{"foo"}, "foo\nbar", true}, - {[]string{"!foo"}, "foo\nbar", false}, - {[]string{"!moo"}, "foo\nbar", true}, - {[]string{"/fo.*/"}, "foo\nbar", true}, - {[]string{"!/fo.*/"}, "foo\nbar", false}, - {[]string{"!/mo.*/"}, "foo\nbar", true}, - {[]string{"foo"}, "", false}, - {[]string{`/\s/tmp\b/`}, "test /tmp bar", true}, + {[]interface{}{""}, "", SUCCESS}, + {[]interface{}{"foo"}, "foo\nbar", SUCCESS}, + {[]interface{}{"!foo"}, "foo\nbar", FAIL}, + {[]interface{}{"!moo"}, "foo\nbar", SUCCESS}, + {[]interface{}{"/fo.*/"}, "foo\nbar", SUCCESS}, + {[]interface{}{"!/fo.*/"}, "foo\nbar", FAIL}, + {[]interface{}{"!/mo.*/"}, "foo\nbar", SUCCESS}, + {[]interface{}{"foo"}, "", FAIL}, + {[]interface{}{`/\s/tmp\b/`}, "test /tmp bar", SUCCESS}, } func TestValidateContains(t *testing.T) { @@ -96,9 +96,9 @@ func TestValidateContains(t *testing.T) { reader := strings.NewReader(c.in2) return reader, nil } - got := ValidateContains(&FakeResource{""}, "", c.in, inFunc, false) - if got.Successful != c.want { - t.Errorf("%+v: got %v, want %v", c, got.Successful, c.want) + got := ValidateValue(&FakeResource{""}, "", c.in, inFunc, false) + if got.Result != c.want { + t.Errorf("%+v: got %v, want %v", c, got.Result, c.want) } } } @@ -109,9 +109,9 @@ func TestValidateContainsErr(t *testing.T) { reader := strings.NewReader(c.in2) return reader, fmt.Errorf("some err") } - got := ValidateContains(&FakeResource{""}, "", c.in, inFunc, false) - if got.Successful != false { - t.Errorf("%+v: got %v, want %v", c, got.Successful, false) + got := ValidateValue(&FakeResource{""}, "", c.in, inFunc, false) + if got.Result != FAIL { + t.Errorf("%+v: got %v, want %v", c, got.Result, FAIL) } } } @@ -121,7 +121,7 @@ func TestValidateContainsBadRegexErr(t *testing.T) { reader := strings.NewReader("dummy") return reader, nil } - got := ValidateContains(&FakeResource{""}, "", []string{"/*\\.* @@.*/"}, inFunc, false) + got := ValidateValue(&FakeResource{""}, "", []interface{}{"/*\\.* @@.*/"}, inFunc, false) if got.Err == nil { t.Errorf("Expected bad regex to raise error, got nil") } @@ -133,7 +133,7 @@ func TestValidateContainsSkip(t *testing.T) { reader := strings.NewReader(c.in2) return reader, nil } - got := ValidateContains(&FakeResource{""}, "", c.in, inFunc, true) + got := ValidateValue(&FakeResource{""}, "", c.in, inFunc, true) if got.Result != SKIP { t.Errorf("%+v: got %v, want %v", c, got.Result, SKIP) } diff --git a/store.go b/store.go index 5c1ce9611..daee475ef 100644 --- a/store.go +++ b/store.go @@ -77,12 +77,12 @@ func (t *TmplVars) Env() map[string]string { func loadVars(varsFile string, varsInline string) (map[string]interface{}, error) { vars, err := varsFromFile(varsFile) if err != nil { - return nil, fmt.Errorf("Error: loading vars file '%s'\n%w", varsFile, err) + return nil, fmt.Errorf("loading vars file '%s'\n%w", varsFile, err) } varsExtra, err := varsFromString(varsInline) if err != nil { - return nil, fmt.Errorf("Error: loading inline vars\n%w", err) + return nil, fmt.Errorf("loading inline vars\n%w", err) } for k, v := range varsExtra { @@ -143,7 +143,7 @@ func ReadJSONData(data []byte, detectFormat bool) (GossConfig, error) { } format := outStoreFormat - if detectFormat == true { + if detectFormat { format, err = getStoreFormatFromData(data) if err != nil { return GossConfig{}, err @@ -311,10 +311,5 @@ func marshalYAML(gossConfig interface{}) ([]byte, error) { } func unmarshalYAML(data []byte, v interface{}) error { - err := yaml.Unmarshal(data, v) - if err != nil { - return fmt.Errorf("could not unmarshal %q as YAML data: %s", string(data), err) - } - - return nil + return yaml.Unmarshal(data, v) } diff --git a/validate.go b/validate.go index 8f254a775..f106d44aa 100644 --- a/validate.go +++ b/validate.go @@ -11,6 +11,7 @@ import ( "time" "github.com/fatih/color" + "github.com/onsi/gomega/format" "github.com/aelsabbahy/goss/outputs" "github.com/aelsabbahy/goss/resource" @@ -100,6 +101,11 @@ func ValidateResults(c *util.Config) (results <-chan []resource.TestResult, err // by the typical CLI invocation and will produce output to StdOut. Use // ValidateResults for programmatic access func Validate(c *util.Config, startTime time.Time) (code int, err error) { + // Needed for contains-elements + // Maybe we don't use this and use custom + // contain_element_matcher is needed because it's single entry to avoid + // transform message + format.UseStringerRepresentation = true outputConfig := util.OutputConfig{ FormatOptions: c.FormatOptions, } From a6d2f2a4a3238c7fdfcf3161cbecc010b6909a51 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 7 Jan 2021 15:55:00 -0800 Subject: [PATCH 10/41] Fix mountinfo splitting when there's a quoted comma --- go.mod | 2 ++ go.sum | 9 +++++++++ integration-tests/goss/centos7/goss.yaml | 12 ------------ integration-tests/goss/goss-shared.yaml | 1 + system/mount.go | 12 +++++++++++- system/mount_test.go | 21 +++++++++++++++++++++ 6 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 system/mount_test.go diff --git a/go.mod b/go.mod index abcca3182..f9523964e 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/onsi/gomega v1.10.4 github.com/opencontainers/runc v0.1.1 github.com/patrickmn/go-cache v2.1.0+incompatible + github.com/pkg/errors v0.9.1 // indirect github.com/shirou/gopsutil v3.20.11+incompatible github.com/stretchr/testify v1.6.1 github.com/thoas/go-funk v0.7.0 @@ -29,6 +30,7 @@ require ( github.com/urfave/cli v1.22.5 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect gopkg.in/yaml.v2 v2.4.0 + gotest.tools/v3 v3.0.3 ) go 1.13 diff --git a/go.sum b/go.sum index a0e3854c9..42ff87ddf 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,9 @@ github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJ github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= @@ -80,6 +83,7 @@ github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= @@ -101,6 +105,7 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHR golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -126,6 +131,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= @@ -149,3 +156,5 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/integration-tests/goss/centos7/goss.yaml b/integration-tests/goss/centos7/goss.yaml index 3710dd360..67fffbe80 100644 --- a/integration-tests/goss/centos7/goss.yaml +++ b/integration-tests/goss/centos7/goss.yaml @@ -29,15 +29,3 @@ process: running: true gossfile: ../goss-s*.yaml: {} -mount: - /dev: - exists: true - opts: - - rw - - nosuid - - context="system_u:object_r:container_file_t:s0:c321 - - c460" - - size=65536k - - mode=755 - source: tmpfs - filesystem: tmpfs diff --git a/integration-tests/goss/goss-shared.yaml b/integration-tests/goss/goss-shared.yaml index ed75773e2..2108a92e0 100644 --- a/integration-tests/goss/goss-shared.yaml +++ b/integration-tests/goss/goss-shared.yaml @@ -172,6 +172,7 @@ mount: opts: - rw - nosuid + - mode=755 source: tmpfs filesystem: tmpfs "/": diff --git a/system/mount.go b/system/mount.go index fb642deb5..80ffdf387 100644 --- a/system/mount.go +++ b/system/mount.go @@ -78,7 +78,7 @@ func (m *DefMount) Opts() ([]string, error) { if err := m.setup(); err != nil { return nil, err } - allOpts := strings.Split(strings.Join([]string{m.mountInfo.Options, m.mountInfo.VFSOptions}, ","), ",") + allOpts := splitMountInfo(strings.Join([]string{m.mountInfo.Options, m.mountInfo.VFSOptions}, ",")) return funk.UniqString(allOpts), nil } @@ -117,3 +117,13 @@ func getMount(mountpoint string) (*mountinfo.Info, error) { } return entries[0], nil } + +func splitMountInfo(s string) []string { + quoted := false + return strings.FieldsFunc(s, func(r rune) bool { + if r == '"' { + quoted = !quoted + } + return !quoted && r == ',' + }) +} diff --git a/system/mount_test.go b/system/mount_test.go new file mode 100644 index 000000000..b9835c555 --- /dev/null +++ b/system/mount_test.go @@ -0,0 +1,21 @@ +package system + +import ( + "testing" + + "gotest.tools/v3/assert" +) + +func TestSplitMountInfo(t *testing.T) { + in := "rw,context=\"system_u:object_r:container_file_t:s0:c174,c741\",size=65536k,mode=755" + want := []string{ + "rw", + "context=\"system_u:object_r:container_file_t:s0:c174,c741\"", + "size=65536k", + "mode=755", + } + + got := splitMountInfo(in) + + assert.DeepEqual(t, got, want) +} From 6bf1c78b47011e9feeae45d8710571f5b0a87bca Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 19 May 2022 08:43:17 -0700 Subject: [PATCH 11/41] Improve have_patterns error message --- matchers/have_patterns.go | 29 +++++++++++++++++++++++------ resource/gomega.go | 12 ++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/matchers/have_patterns.go b/matchers/have_patterns.go index 8546a7842..112a80bb8 100644 --- a/matchers/have_patterns.go +++ b/matchers/have_patterns.go @@ -31,7 +31,7 @@ func HavePatterns(elements interface{}) GossMatcher { func (m *HavePatternsMatcher) Match(actual interface{}) (success bool, err error) { t, ok := m.Elements.([]interface{}) if !ok { - return false, fmt.Errorf("HavePatterns matcher expects an io.reader. Got:\n%s", format.Object(actual, 1)) + return false, fmt.Errorf("HavePatterns matcher expects an array of matchers. Got:\n%s", format.Object(m.Elements, 1)) } elements := make([]string, len(t)) for i, v := range t { @@ -50,9 +50,18 @@ func (m *HavePatternsMatcher) Match(actual interface{}) (success bool, err error if len(notfound) == 0 { return true, nil } - fh, ok := actual.(io.Reader) - if !ok { - return false, fmt.Errorf("Incorrect type") + var fh io.Reader + switch av := actual.(type) { + case io.Reader: + fh = av + case string: + fh = strings.NewReader(av) + default: + err = fmt.Errorf("Incorrect type %T", actual) + + } + if err != nil { + return false, err } defer func() { @@ -105,8 +114,12 @@ func (m *HavePatternsMatcher) Match(actual interface{}) (success bool, err error } func (m *HavePatternsMatcher) FailureResult(actual interface{}) MatcherResult { + a, ok := actual.(string) + if !ok { + a = fmt.Sprintf("object: %T", actual) + } return MatcherResult{ - Actual: fmt.Sprintf("object: %T", actual), + Actual: a, Message: "to contain patterns", Expected: m.Elements, MissingElements: m.missingElements, @@ -114,8 +127,12 @@ func (m *HavePatternsMatcher) FailureResult(actual interface{}) MatcherResult { } func (m *HavePatternsMatcher) NegatedFailureResult(actual interface{}) MatcherResult { + a, ok := actual.(string) + if !ok { + a = fmt.Sprintf("object: %T", actual) + } return MatcherResult{ - Actual: fmt.Sprintf("object: %T", actual), + Actual: a, Message: "not to contain patterns", Expected: m.Elements, } diff --git a/resource/gomega.go b/resource/gomega.go index 6ace6cf4a..e4bf9e254 100644 --- a/resource/gomega.go +++ b/resource/gomega.go @@ -2,6 +2,7 @@ package resource import ( "fmt" + "log" "github.com/aelsabbahy/goss/matchers" ) @@ -51,6 +52,12 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { return matchers.WithSafeTransform(matchers.ToString{}, matchers.ContainSubstring(value.(string))), nil case "have-len": return matchers.HaveLen(int(value.(float64))), nil + case "have-patterns": + _, isArr := value.([]interface{}) + if !isArr { + return nil, fmt.Errorf("have-patterns: incorrect expectation type, expected array, got: %t", value) + } + return matchers.WithSafeTransform(matchers.ToString{}, matchers.HavePatterns(value)), nil case "have-key": subMatcher, err := matcherToGomegaMatcher(value) if err != nil { @@ -58,7 +65,12 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { } return matchers.HaveKey(subMatcher), nil case "contain-element": + _, isArr := value.([]interface{}) + if isArr { + return nil, fmt.Errorf("contain-element: incorrect expectation type, expected matcher or value, got: %t", value) + } subMatcher, err := matcherToGomegaMatcher(value) + log.Printf("output: %#v", value) if err != nil { return nil, err } From 4be04f9091638c185d9f35be2119e566b186919e Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 5 Sep 2022 08:08:00 -0700 Subject: [PATCH 12/41] Add syntax checks for type casting --- resource/gomega.go | 51 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/resource/gomega.go b/resource/gomega.go index e4bf9e254..2a81f4932 100644 --- a/resource/gomega.go +++ b/resource/gomega.go @@ -2,12 +2,12 @@ package resource import ( "fmt" - "log" "github.com/aelsabbahy/goss/matchers" ) func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { + // Default matchers switch x := matcher.(type) { case string: return matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal(x)), nil @@ -43,19 +43,39 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { case "equal": return matchers.Equal(value), nil case "have-prefix": - return matchers.WithSafeTransform(matchers.ToString{}, matchers.HavePrefix(value.(string))), nil + v, isStr := value.(string) + if !isStr { + return nil, fmt.Errorf("have-prefix: syntax error: incorrect expectation type. expected string, got: %#v", value) + } + return matchers.WithSafeTransform(matchers.ToString{}, matchers.HavePrefix(v)), nil case "have-suffix": - return matchers.WithSafeTransform(matchers.ToString{}, matchers.HaveSuffix(value.(string))), nil + v, isStr := value.(string) + if !isStr { + return nil, fmt.Errorf("have-suffix: syntax error: incorrect expectation type. expected string, got: %#v", value) + } + return matchers.WithSafeTransform(matchers.ToString{}, matchers.HaveSuffix(v)), nil case "match-regexp": - return matchers.WithSafeTransform(matchers.ToString{}, matchers.MatchRegexp(value.(string))), nil + v, isStr := value.(string) + if !isStr { + return nil, fmt.Errorf("match-regexp: syntax error: incorrect expectation type. expected string, got: %#v", value) + } + return matchers.WithSafeTransform(matchers.ToString{}, matchers.MatchRegexp(v)), nil case "contain-substring": - return matchers.WithSafeTransform(matchers.ToString{}, matchers.ContainSubstring(value.(string))), nil + v, isStr := value.(string) + if !isStr { + return nil, fmt.Errorf("contain-substring: syntax error: incorrect expectation type. expected string, got: %#v", value) + } + return matchers.WithSafeTransform(matchers.ToString{}, matchers.ContainSubstring(v)), nil case "have-len": - return matchers.HaveLen(int(value.(float64))), nil + v, isFloat := value.(float64) + if !isFloat { + return nil, fmt.Errorf("have-len: syntax error: incorrect expectation type. expected numeric, got: %#v", value) + } + return matchers.HaveLen(int(v)), nil case "have-patterns": _, isArr := value.([]interface{}) if !isArr { - return nil, fmt.Errorf("have-patterns: incorrect expectation type, expected array, got: %t", value) + return nil, fmt.Errorf("have-patterns: syntax error: incorrect expectation type. expected array, got: %#v", value) } return matchers.WithSafeTransform(matchers.ToString{}, matchers.HavePatterns(value)), nil case "have-key": @@ -65,12 +85,12 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { } return matchers.HaveKey(subMatcher), nil case "contain-element": - _, isArr := value.([]interface{}) - if isArr { - return nil, fmt.Errorf("contain-element: incorrect expectation type, expected matcher or value, got: %t", value) + switch value.(type) { + case map[string]interface{}, string: + default: + return nil, fmt.Errorf("contain-element: syntax error: incorrect expectation type. expected matcher or value, got: %#v", value) } subMatcher, err := matcherToGomegaMatcher(value) - log.Printf("output: %#v", value) if err != nil { return nil, err } @@ -124,7 +144,11 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { return matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(comparator, value)), nil case "semver-constraint": - return matchers.BeSemverConstraint(value.(string)), nil + v, isStr := value.(string) + if !isStr { + return nil, fmt.Errorf("semver-contstraint: syntax error: incorrect expectation type. expected string, got: %#v", value) + } + return matchers.BeSemverConstraint(v), nil case "gjson": var subMatchers []matchers.GossMatcher valueI, ok := value.(map[string]interface{}) @@ -162,7 +186,7 @@ func sliceToGomega(value interface{}) ([]matchers.GossMatcher, error) { return subMatchers, nil } -// Normalize expectedValue so json and yaml are the same +// sanitizeExpectedValue normalizes the value so json and yaml are the same func sanitizeExpectedValue(i interface{}) interface{} { if e, ok := i.(int); ok { return float64(e) @@ -172,6 +196,7 @@ func sanitizeExpectedValue(i interface{}) interface{} { for k, v := range e { ks, ok := k.(string) if !ok { + // We should never get here panic(fmt.Sprintf("Matcher key type not string: %T\n\n", k)) } out[ks] = sanitizeExpectedValue(v) From dfb118611b683fc1a065b61caf7535fdddd11f6d Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 5 Sep 2022 09:07:44 -0700 Subject: [PATCH 13/41] Rename file.contains to file.contents --- docs/manual.md | 8 ++++---- resource/file.go | 14 +++++++++++--- resource/resource.go | 3 ++- system/file.go | 4 ++-- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/docs/manual.md b/docs/manual.md index e8fc0b7ac..b1b841ab5 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -563,7 +563,7 @@ file: owner: root group: root filetype: file # file, symlink, directory - contains: [] # Check file content for these patterns + contents: [] # Check file content for these patterns md5: 7c9bb14b3bf178e82c00c2a4398c93cd # md5 checksum of file # A stronger checksum alternatives to md5 (recommended) sha256: 7f78ce27859049f725936f7b52c6e25d774012947d915e7b394402cfceb70c4c @@ -577,7 +577,7 @@ file: skip: false ``` -`contains` can be a string or a [pattern](#patterns) +`contents` can be a string or a [pattern](#patterns) ### gossfile @@ -890,7 +890,7 @@ Example: file: /tmp/test.txt: exists: true - contains: + contents: - "foo" - "!bar" - "/[Gg]oss/" @@ -902,7 +902,7 @@ The above can be expressed as: file: /tmp/test.txt: exists: true - contains: + contents: and: - contain-element: "foo" - not: {contain-element: "bar"} diff --git a/resource/file.go b/resource/file.go index eef5c304a..c7403dba1 100644 --- a/resource/file.go +++ b/resource/file.go @@ -1,6 +1,9 @@ package resource import ( + "fmt" + "os" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) @@ -16,7 +19,8 @@ type File struct { Group matcher `json:"group,omitempty" yaml:"group,omitempty"` LinkedTo matcher `json:"linked-to,omitempty" yaml:"linked-to,omitempty"` Filetype matcher `json:"filetype,omitempty" yaml:"filetype,omitempty"` - Contains matcher `json:"contains" yaml:"contains"` + Contains matcher `json:"contains,omitempty" yaml:"contains,omitempty"` + Contents matcher `json:"contents" yaml:"contents"` Md5 matcher `json:"md5,omitempty" yaml:"md5,omitempty"` Sha256 matcher `json:"sha256,omitempty" yaml:"sha256,omitempty"` Sha512 matcher `json:"sha512,omitempty" yaml:"sha512,omitempty"` @@ -58,7 +62,11 @@ func (f *File) Validate(sys *system.System) []TestResult { results = append(results, ValidateValue(f, "filetype", f.Filetype, sysFile.Filetype, skip)) } if isSet(f.Contains) { - results = append(results, ValidateValue(f, "contains", f.Contains, sysFile.Contains, skip)) + fmt.Fprintf(os.Stderr, "DEPRECATION WARNING: file.contains has been renamed to file.contents\n") + results = append(results, ValidateValue(f, "contains", f.Contains, sysFile.Contents, skip)) + } + if isSet(f.Contents) { + results = append(results, ValidateValue(f, "contents", f.Contents, sysFile.Contents, skip)) } if f.Size != nil { results = append(results, ValidateValue(f, "size", f.Size, sysFile.Size, skip)) @@ -84,7 +92,7 @@ func NewFile(sysFile system.File, config util.Config) (*File, error) { f := &File{ Path: path, Exists: exists, - Contains: []string{}, + Contents: []string{}, } if !contains(config.IgnoreList, "mode") { if mode, err := sysFile.Mode(); err == nil { diff --git a/resource/resource.go b/resource/resource.go index 5b5e33638..888fb1ac6 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -2,6 +2,7 @@ package resource import ( "fmt" + "os" "path/filepath" "strconv" "strings" @@ -64,7 +65,7 @@ func deprecateAtoI(depr interface{}, desc string) interface{} { if !ok { return depr } - fmt.Printf("DEPRECATION WARNING: %s should be an integer not a string\n", desc) + fmt.Fprintf(os.Stderr, "DEPRECATION WARNING: %s should be an integer not a string\n", desc) i, err := strconv.Atoi(s) if err != nil { panic(err) diff --git a/system/file.go b/system/file.go index 6861ed7b3..a1b78ba7c 100644 --- a/system/file.go +++ b/system/file.go @@ -19,7 +19,7 @@ import ( type File interface { Path() string Exists() (bool, error) - Contains() (io.Reader, error) + Contents() (io.Reader, error) Mode() (string, error) Size() (int, error) Filetype() (string, error) @@ -83,7 +83,7 @@ func (f *DefFile) Exists() (bool, error) { return true, err } -func (f *DefFile) Contains() (io.Reader, error) { +func (f *DefFile) Contents() (io.Reader, error) { if err := f.setup(); err != nil { return nil, err } From 12cef659fced2f9d97e92e526eda5090e56b6fbb Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 5 Sep 2022 09:52:01 -0700 Subject: [PATCH 14/41] ToString converter add suppert for []interface{} --- matchers/type_conversion.go | 6 ++++++ resource/matching.go | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/matchers/type_conversion.go b/matchers/type_conversion.go index c0838da6c..0999ef49f 100644 --- a/matchers/type_conversion.go +++ b/matchers/type_conversion.go @@ -47,6 +47,12 @@ type ToString struct{} func (t ToString) Transform(e interface{}) (interface{}, error) { switch v := e.(type) { + case []interface{}: + vs := make([]string, len(v)) + for i, v := range v { + vs[i] = fmt.Sprintf("%v", v) + } + return strings.Join(vs, "\n"), nil case []string: return strings.Join(v, "\n"), nil default: diff --git a/resource/matching.go b/resource/matching.go index 5b686cc45..040fc3f56 100644 --- a/resource/matching.go +++ b/resource/matching.go @@ -34,9 +34,10 @@ func (a *Matching) Validate(sys *system.System) []TestResult { var stub interface{} if a.AsReader { + s := fmt.Sprintf("%v", a.Content) // ValidateValue expects a function stub = func() (io.Reader, error) { - return strings.NewReader(a.Content.(string)), nil + return strings.NewReader(s), nil } } else { // ValidateValue expects a function From 1fe5571b6a47735e15ed313f38618c32bd3dc919 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Wed, 14 Sep 2022 21:17:52 -0700 Subject: [PATCH 15/41] Convert headers to lowercase. fixes #760 --- matchers/have_patterns.go | 9 +++++++-- resource/http.go | 6 +++--- system/http.go | 11 ++++++----- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/matchers/have_patterns.go b/matchers/have_patterns.go index 112a80bb8..cd892e966 100644 --- a/matchers/have_patterns.go +++ b/matchers/have_patterns.go @@ -56,6 +56,8 @@ func (m *HavePatternsMatcher) Match(actual interface{}) (success bool, err error fh = av case string: fh = strings.NewReader(av) + case []string: + fh = strings.NewReader(strings.Join(av, "\n")) default: err = fmt.Errorf("Incorrect type %T", actual) @@ -114,8 +116,11 @@ func (m *HavePatternsMatcher) Match(actual interface{}) (success bool, err error } func (m *HavePatternsMatcher) FailureResult(actual interface{}) MatcherResult { - a, ok := actual.(string) - if !ok { + var a interface{} + switch actual.(type) { + case string, []string: + a = actual + default: a = fmt.Sprintf("object: %T", actual) } return MatcherResult{ diff --git a/resource/http.go b/resource/http.go index 92931977b..c82896217 100644 --- a/resource/http.go +++ b/resource/http.go @@ -16,11 +16,11 @@ type HTTP struct { 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"` + Timeout int `json:"timeout,omitempty" yaml:"timeout,omitempty"` RequestHeader []string `json:"request-headers,omitempty" yaml:"request-headers,omitempty"` RequestBody string `json:"request-bod,omitemptyy" yaml:"request-body,omitempty"` Headers matcher `json:"headers,omitempty" yaml:"headers,omitempty"` - Body matcher `json:"body" yaml:"body"` + Body matcher `json:"body,omitempty" yaml:"body,omitempty"` Username string `json:"username,omitempty" yaml:"username,omitempty"` Password string `json:"password,omitempty" yaml:"password,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` @@ -64,7 +64,7 @@ func (u *HTTP) Validate(sys *system.System) []TestResult { skip = true } if isSet(u.Headers) { - results = append(results, ValidateGomegaValue(u, "Headers", u.Headers, sysHTTP.Headers, skip)) + results = append(results, ValidateValue(u, "Headers", u.Headers, sysHTTP.Headers, skip)) } if isSet(u.Body) { results = append(results, ValidateValue(u, "Body", u.Body, sysHTTP.Body, skip)) diff --git a/system/http.go b/system/http.go index 3e667eaeb..40ae4187e 100644 --- a/system/http.go +++ b/system/http.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "net/url" + "sort" "strings" "time" @@ -15,7 +16,7 @@ import ( type HTTP interface { HTTP() string Status() (int, error) - Headers() (io.Reader, error) + Headers() ([]string, error) Body() (io.Reader, error) Exists() (bool, error) SetAllowInsecure(bool) @@ -61,9 +62,10 @@ func NewDefHTTP(httpStr string, system *System, config util.Config) HTTP { func HeaderToArray(header http.Header) (res []string) { for name, values := range header { for _, value := range values { - res = append(res, fmt.Sprintf("%s: %s", name, value)) + res = append(res, strings.ToLower(fmt.Sprintf("%s: %s", name, value))) } } + sort.Strings(res) return } @@ -149,13 +151,12 @@ func (u *DefHTTP) Status() (int, error) { return u.resp.StatusCode, nil } -func (u *DefHTTP) Headers() (io.Reader, error) { +func (u *DefHTTP) Headers() ([]string, error) { if err := u.setup(); err != nil { return nil, err } - var headerString = strings.Join(HeaderToArray(u.resp.Header), "\n") - return strings.NewReader(headerString), nil + return HeaderToArray(u.resp.Header), nil } func (u *DefHTTP) Body() (io.Reader, error) { From 2ebb1f8ecd8f75e644178ebcdfa63d32dc714191 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Wed, 14 Sep 2022 21:28:00 -0700 Subject: [PATCH 16/41] Fix contains -> contents, lowercase headers and contain-element float64 --- integration-tests/goss/alpine3/goss-expected-q.yaml | 4 ++-- integration-tests/goss/alpine3/goss-expected.yaml | 4 ++-- integration-tests/goss/centos7/goss-expected-q.yaml | 4 ++-- integration-tests/goss/centos7/goss-expected.yaml | 4 ++-- integration-tests/goss/darwin/tests/file.goss.yaml | 2 +- integration-tests/goss/darwin/tests/http.goss.yaml | 2 +- integration-tests/goss/goss-shared.yaml | 10 +++++----- integration-tests/goss/trusty/goss-expected-q.yaml | 4 ++-- integration-tests/goss/trusty/goss-expected.yaml | 4 ++-- integration-tests/goss/wheezy/goss-expected-q.yaml | 4 ++-- integration-tests/goss/wheezy/goss-expected.yaml | 4 ++-- integration-tests/goss/windows/tests/file.goss.yaml | 2 +- integration-tests/goss/windows/tests/http.goss.yaml | 2 +- resource/gomega.go | 2 +- 14 files changed, 26 insertions(+), 26 deletions(-) diff --git a/integration-tests/goss/alpine3/goss-expected-q.yaml b/integration-tests/goss/alpine3/goss-expected-q.yaml index 9aa197cbd..703fdd573 100644 --- a/integration-tests/goss/alpine3/goss-expected-q.yaml +++ b/integration-tests/goss/alpine3/goss-expected-q.yaml @@ -1,10 +1,10 @@ file: /etc/passwd: exists: true - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: apache2: installed: true diff --git a/integration-tests/goss/alpine3/goss-expected.yaml b/integration-tests/goss/alpine3/goss-expected.yaml index cb175b59d..6a0fbb6f5 100644 --- a/integration-tests/goss/alpine3/goss-expected.yaml +++ b/integration-tests/goss/alpine3/goss-expected.yaml @@ -5,10 +5,10 @@ file: owner: root group: root filetype: file - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: apache2: installed: true diff --git a/integration-tests/goss/centos7/goss-expected-q.yaml b/integration-tests/goss/centos7/goss-expected-q.yaml index 345c983e8..46698a33a 100644 --- a/integration-tests/goss/centos7/goss-expected-q.yaml +++ b/integration-tests/goss/centos7/goss-expected-q.yaml @@ -1,10 +1,10 @@ file: /etc/passwd: exists: true - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: foobar: installed: false diff --git a/integration-tests/goss/centos7/goss-expected.yaml b/integration-tests/goss/centos7/goss-expected.yaml index a179f55c8..5bbd0108b 100644 --- a/integration-tests/goss/centos7/goss-expected.yaml +++ b/integration-tests/goss/centos7/goss-expected.yaml @@ -5,10 +5,10 @@ file: owner: root group: root filetype: file - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: foobar: installed: false diff --git a/integration-tests/goss/darwin/tests/file.goss.yaml b/integration-tests/goss/darwin/tests/file.goss.yaml index 0f428a235..347494ea4 100644 --- a/integration-tests/goss/darwin/tests/file.goss.yaml +++ b/integration-tests/goss/darwin/tests/file.goss.yaml @@ -9,6 +9,6 @@ file: filetype: file md5: 9dcea4037b1439a2a96e4d206eda63a4 sha256: e73d885411a52a0d29142e830e104e0cc9252fbb1dc3c92a430ef7c369f089ef - contains: + contents: - "nothing to see here" - "/nothing.*here/" diff --git a/integration-tests/goss/darwin/tests/http.goss.yaml b/integration-tests/goss/darwin/tests/http.goss.yaml index 3ba7b60e9..74191fe35 100644 --- a/integration-tests/goss/darwin/tests/http.goss.yaml +++ b/integration-tests/goss/darwin/tests/http.goss.yaml @@ -8,7 +8,7 @@ http: request-headers: - "Content-Type: text/html" headers: - - "Content-Type: text/html" + - "content-type: text/html" body: - "google" username: "" diff --git a/integration-tests/goss/goss-shared.yaml b/integration-tests/goss/goss-shared.yaml index 2108a92e0..2e4f86bb3 100644 --- a/integration-tests/goss/goss-shared.yaml +++ b/integration-tests/goss/goss-shared.yaml @@ -25,7 +25,7 @@ file: owner: root group: root filetype: file - contains: + contents: - root {{end}} "/goss/hellogoss.txt": @@ -35,7 +35,7 @@ file: sha512: 372864ab83187de41ca57c5c77cd4a99220ccadc8b8ddb18367893fd3e58764193a599edbf63a48c0c44f1e923606a00929b46de3bda1744fd722b9d42829206 "/tmp/goss/foobar": exists: false - contains: [] + contents: [] "~root": exists: true mode: '0700' @@ -50,7 +50,7 @@ file: filetype: pipe "/does/not/exist": exists: true - contains: + contents: - skip-this-test skip: true package: @@ -199,7 +199,7 @@ http: timeout: 5000 request-headers: - "Foo: bar" - headers: ["Content-Type: application/json"] + headers: ["content-type: application/json"] body: ['"Foo": "bar"'] https://httpbin.org/headers?host: status: 200 @@ -210,7 +210,7 @@ http: # need to see if there's a good way around this, maybe local httpbin? #- "Host: example.com" - "Host: httpbin.org" - headers: ["Content-Type: application/json"] + headers: ["content-type: application/json"] # body: ['"Host": "example.com"'] body: ['"Host": "httpbin.org"'] https://httpbin.org/basic-auth/username/secret: diff --git a/integration-tests/goss/trusty/goss-expected-q.yaml b/integration-tests/goss/trusty/goss-expected-q.yaml index e37715a76..3f166ce92 100644 --- a/integration-tests/goss/trusty/goss-expected-q.yaml +++ b/integration-tests/goss/trusty/goss-expected-q.yaml @@ -1,10 +1,10 @@ file: /etc/passwd: exists: true - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: apache2: installed: true diff --git a/integration-tests/goss/trusty/goss-expected.yaml b/integration-tests/goss/trusty/goss-expected.yaml index fbc6b3d7c..34620a557 100644 --- a/integration-tests/goss/trusty/goss-expected.yaml +++ b/integration-tests/goss/trusty/goss-expected.yaml @@ -5,10 +5,10 @@ file: owner: root group: root filetype: file - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: apache2: installed: true diff --git a/integration-tests/goss/wheezy/goss-expected-q.yaml b/integration-tests/goss/wheezy/goss-expected-q.yaml index e37715a76..3f166ce92 100644 --- a/integration-tests/goss/wheezy/goss-expected-q.yaml +++ b/integration-tests/goss/wheezy/goss-expected-q.yaml @@ -1,10 +1,10 @@ file: /etc/passwd: exists: true - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: apache2: installed: true diff --git a/integration-tests/goss/wheezy/goss-expected.yaml b/integration-tests/goss/wheezy/goss-expected.yaml index d93a2b67f..37d886cd1 100644 --- a/integration-tests/goss/wheezy/goss-expected.yaml +++ b/integration-tests/goss/wheezy/goss-expected.yaml @@ -5,10 +5,10 @@ file: owner: root group: root filetype: file - contains: [] + contents: [] /tmp/goss/foobar: exists: false - contains: [] + contents: [] package: apache2: installed: true diff --git a/integration-tests/goss/windows/tests/file.goss.yaml b/integration-tests/goss/windows/tests/file.goss.yaml index 80f1b7508..a1c3ed0b4 100644 --- a/integration-tests/goss/windows/tests/file.goss.yaml +++ b/integration-tests/goss/windows/tests/file.goss.yaml @@ -9,6 +9,6 @@ file: filetype: file md5: dc9a07ca9789f866d21d544fe5651954 sha256: aa8b1b4a0d9bf174f5019c8f8a9568858ee2bdf8e0ad16aec54417d49b48df49 - contains: + contents: - "nothing to see here" - "/nothing.*here/" diff --git a/integration-tests/goss/windows/tests/http.goss.yaml b/integration-tests/goss/windows/tests/http.goss.yaml index 3ba7b60e9..74191fe35 100644 --- a/integration-tests/goss/windows/tests/http.goss.yaml +++ b/integration-tests/goss/windows/tests/http.goss.yaml @@ -8,7 +8,7 @@ http: request-headers: - "Content-Type: text/html" headers: - - "Content-Type: text/html" + - "content-type: text/html" body: - "google" username: "" diff --git a/resource/gomega.go b/resource/gomega.go index 2a81f4932..6d7c8453e 100644 --- a/resource/gomega.go +++ b/resource/gomega.go @@ -86,7 +86,7 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { return matchers.HaveKey(subMatcher), nil case "contain-element": switch value.(type) { - case map[string]interface{}, string: + case map[string]interface{}, string, float64: default: return nil, fmt.Errorf("contain-element: syntax error: incorrect expectation type. expected matcher or value, got: %#v", value) } From bf22f82a0518ae52121879a38e06bef26c1bb79b Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Fri, 23 Sep 2022 18:21:29 -0700 Subject: [PATCH 17/41] Enhance all resources to support key override closes #518, closes #742 --- docs/manual.md | 35 ++++++++++++++++++++++++++++++++--- go.mod | 1 + go.sum | 4 ++-- resource/addr.go | 27 ++++++++++++++++++++------- resource/command.go | 12 ++++++------ resource/dns.go | 23 ++++++++++++++++++----- resource/file.go | 22 +++++++++++++++++----- resource/group.go | 26 +++++++++++++++++++------- resource/http.go | 19 ++++++++++++------- resource/interface.go | 24 +++++++++++++++++++----- resource/kernel_param.go | 31 +++++++++++++++++++++++-------- resource/matching.go | 6 +++--- resource/mount.go | 26 ++++++++++++++++++++------ resource/package.go | 24 +++++++++++++++++++----- resource/port.go | 24 +++++++++++++++++++----- resource/process.go | 34 ++++++++++++++++++++++++---------- resource/service.go | 24 +++++++++++++++++++----- resource/user.go | 24 ++++++++++++++++++------ 18 files changed, 291 insertions(+), 95 deletions(-) diff --git a/docs/manual.md b/docs/manual.md index b1b841ab5..8c678688e 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -453,9 +453,12 @@ Validates if a remote `address:port` are accessible. ```yaml addr: tcp://ip-address-or-domain-name:80: + # required attributes reachable: true - timeout: 500 # optional attributes + # defaults to hash key + address: "tcp://ip-address-or-domain-name:80" + timeout: 500 local-address: 127.0.0.1 ``` @@ -465,12 +468,12 @@ Validates the exit-status and output of a command. This can be used in combinati ```yaml command: - version: + 'go version': # required attributes exit-status: 0 + # optional attributes # defaults to hash key exec: "go version" - # optional attributes stdout: - go version go1.6 linux/amd64 stderr: [] @@ -492,6 +495,8 @@ dns: # required attributes resolvable: true # optional attributes + # defaults to hash key + resolve: localhost addrs: - 127.0.0.1 - ::1 @@ -558,6 +563,8 @@ file: # required attributes exists: true # optional attributes + # defaults to hash key + path: /etc/passwd mode: "0644" size: 2118 # in bytes owner: root @@ -599,6 +606,8 @@ group: # required attributes exists: true # optional attributes + # defaults to hash key + groupname: /etc/passwd gid: 65534 skip: false ``` @@ -613,6 +622,8 @@ http: # required attributes status: 200 # optional attributes + # defaults to hash key + url: https://www.google.com allow-insecure: false no-follow-redirects: false # Setting this to true will NOT follow redirects timeout: 1000 @@ -639,6 +650,8 @@ interface: # required attributes exists: true # optional attributes + # defaults to hash key + name: eth0 addrs: - 172.17.0.2/16 - fe80::42:acff:fe11:2/64 @@ -654,6 +667,9 @@ kernel-param: kernel.ostype: # required attributes value: Linux + # optional attributes + # defaults to hash key + name: kernel.ostype ``` To see the full list of current values, run `sysctl -a`. @@ -668,6 +684,8 @@ mount: # required attributes exists: true # optional attributes + # defaults to hash key + mountpoint: /home opts: - rw - relatime @@ -742,6 +760,8 @@ package: # required attributes installed: true # optional attributes + # defaults to hash key + name: httpd versions: - 2.2.15 skip: false @@ -762,6 +782,8 @@ port: # required attributes listening: true # optional attributes + # defaults to hash key + port: 'tcp:22' ip: # what IP(s) is it listening on - 0.0.0.0 skip: false @@ -776,6 +798,9 @@ process: chrome: # required attributes running: true + # optional attributes + # defaults to hash key + comm: chrome skip: false ``` @@ -786,6 +811,8 @@ Validates the state of a service. service: sshd: # Optional attributes + # defaults to hash key + name: sshd enabled: true running: true runlevels: ["3", "4", "5"] # Alpine example, runlevels: ["default"] @@ -806,6 +833,8 @@ user: # required attributes exists: true # optional attributes + # defaults to hash key + username: nfsnobody uid: 65534 gid: 65534 groups: diff --git a/go.mod b/go.mod index f9523964e..fbcab0fd5 100644 --- a/go.mod +++ b/go.mod @@ -29,6 +29,7 @@ require ( github.com/tidwall/gjson v1.6.7 github.com/urfave/cli v1.22.5 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect + golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.0.3 ) diff --git a/go.sum b/go.sum index 42ff87ddf..1f61d67d6 100644 --- a/go.sum +++ b/go.sum @@ -124,15 +124,15 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= +golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= diff --git a/resource/addr.go b/resource/addr.go index 37826db36..7b819bc88 100644 --- a/resource/addr.go +++ b/resource/addr.go @@ -1,6 +1,7 @@ package resource import ( + "fmt" "time" "github.com/aelsabbahy/goss/system" @@ -10,18 +11,30 @@ import ( type Addr struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Address string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Address string `json:"address,omitempty" yaml:"address,omitempty"` LocalAddress string `json:"local-address,omitempty" yaml:"local-address,omitempty"` Reachable matcher `json:"reachable" yaml:"reachable"` Timeout int `json:"timeout" yaml:"timeout"` } -func (a *Addr) ID() string { return a.Address } -func (a *Addr) SetID(id string) { a.Address = id } +func (a *Addr) ID() string { + if a.Address != "" && a.Address != a.id { + return fmt.Sprintf("%s: %s", a.id, a.Address) + } + return a.id +} +func (a *Addr) SetID(id string) { a.id = id } // FIXME: Can this be refactored? -func (r *Addr) GetTitle() string { return r.Title } -func (r *Addr) GetMeta() meta { return r.Meta } +func (a *Addr) GetTitle() string { return a.Title } +func (a *Addr) GetMeta() meta { return a.Meta } +func (a *Addr) GetAddress() string { + if a.Address != "" { + return a.Address + } + return a.id +} func (a *Addr) Validate(sys *system.System) []TestResult { skip := false @@ -29,7 +42,7 @@ func (a *Addr) Validate(sys *system.System) []TestResult { a.Timeout = 500 } - sysAddr := sys.NewAddr(a.Address, sys, util.Config{Timeout: time.Duration(a.Timeout) * time.Millisecond, LocalAddress: a.LocalAddress}) + sysAddr := sys.NewAddr(a.GetAddress(), sys, util.Config{Timeout: time.Duration(a.Timeout) * time.Millisecond, LocalAddress: a.LocalAddress}) var results []TestResult results = append(results, ValidateValue(a, "reachable", a.Reachable, sysAddr.Reachable, skip)) @@ -40,7 +53,7 @@ func NewAddr(sysAddr system.Addr, config util.Config) (*Addr, error) { address := sysAddr.Address() reachable, err := sysAddr.Reachable() a := &Addr{ - Address: address, + id: address, Reachable: reachable, Timeout: config.TimeOutMilliSeconds(), LocalAddress: config.LocalAddress, diff --git a/resource/command.go b/resource/command.go index a667dfae0..30ab6ecdd 100644 --- a/resource/command.go +++ b/resource/command.go @@ -14,7 +14,7 @@ import ( type Command struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Command string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` Exec string `json:"exec,omitempty" yaml:"exec,omitempty"` ExitStatus matcher `json:"exit-status" yaml:"exit-status"` Stdout matcher `json:"stdout" yaml:"stdout"` @@ -23,8 +23,8 @@ type Command struct { Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (c *Command) ID() string { return c.Command } -func (c *Command) SetID(id string) { c.Command = id } +func (c *Command) ID() string { return c.id } +func (c *Command) SetID(id string) { c.id = id } func (c *Command) GetTitle() string { return c.Title } func (c *Command) GetMeta() meta { return c.Meta } @@ -32,7 +32,7 @@ func (c *Command) GetExec() string { if c.Exec != "" { return c.Exec } - return c.Command + return c.id } func (c *Command) Validate(sys *system.System) []TestResult { @@ -47,7 +47,7 @@ func (c *Command) Validate(sys *system.System) []TestResult { var results []TestResult sysCommand := sys.NewCommand(c.GetExec(), sys, util.Config{Timeout: time.Duration(c.Timeout) * time.Millisecond}) - cExitStatus := deprecateAtoI(c.ExitStatus, fmt.Sprintf("%s: command.exit-status", c.Command)) + cExitStatus := deprecateAtoI(c.ExitStatus, fmt.Sprintf("%s: command.exit-status", c.ID())) results = append(results, ValidateValue(c, "exit-status", cExitStatus, sysCommand.ExitStatus, skip)) if isSet(c.Stdout) { results = append(results, ValidateValue(c, "stdout", c.Stdout, sysCommand.Stdout, skip)) @@ -62,7 +62,7 @@ func NewCommand(sysCommand system.Command, config util.Config) (*Command, error) command := sysCommand.Command() exitStatus, err := sysCommand.ExitStatus() c := &Command{ - Command: command, + id: command, ExitStatus: exitStatus, Stdout: []string{}, Stderr: []string{}, diff --git a/resource/dns.go b/resource/dns.go index ad2fcec93..653aecfaa 100644 --- a/resource/dns.go +++ b/resource/dns.go @@ -1,6 +1,7 @@ package resource import ( + "fmt" "strings" "time" @@ -11,7 +12,8 @@ import ( type DNS struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Host string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Resolve string `json:"resolve,omitempty" yaml:"resolve,omitempty"` Resolveable matcher `json:"resolveable,omitempty" yaml:"resolveable,omitempty"` Resolvable matcher `json:"resolvable" yaml:"resolvable"` Addrs matcher `json:"addrs,omitempty" yaml:"addrs,omitempty"` @@ -20,11 +22,22 @@ type DNS struct { Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (d *DNS) ID() string { return d.Host } -func (d *DNS) SetID(id string) { d.Host = id } +func (d *DNS) ID() string { + if d.Resolve != "" && d.Resolve != d.id { + return fmt.Sprintf("%s: %s", d.id, d.Resolve) + } + return d.id +} +func (d *DNS) SetID(id string) { d.id = id } func (d *DNS) GetTitle() string { return d.Title } func (d *DNS) GetMeta() meta { return d.Meta } +func (d *DNS) GetResolve() string { + if d.Resolve != "" { + return d.Resolve + } + return d.id +} func (d *DNS) Validate(sys *system.System) []TestResult { skip := false @@ -35,7 +48,7 @@ func (d *DNS) Validate(sys *system.System) []TestResult { skip = true } - sysDNS := sys.NewDNS(d.Host, sys, util.Config{Timeout: time.Duration(d.Timeout) * time.Millisecond, Server: d.Server}) + sysDNS := sys.NewDNS(d.GetResolve(), sys, util.Config{Timeout: time.Duration(d.Timeout) * time.Millisecond, Server: d.Server}) var results []TestResult // Backwards copatibility hack for now @@ -64,7 +77,7 @@ func NewDNS(sysDNS system.DNS, config util.Config) (*DNS, error) { server := sysDNS.Server() d := &DNS{ - Host: host, + id: host, Resolvable: resolvable, Timeout: config.TimeOutMilliSeconds(), Server: server, diff --git a/resource/file.go b/resource/file.go index c7403dba1..1462c811f 100644 --- a/resource/file.go +++ b/resource/file.go @@ -11,7 +11,8 @@ import ( type File struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Path string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Path string `json:"path,omitempty" yaml:"path,omitempty"` Exists matcher `json:"exists" yaml:"exists"` Mode matcher `json:"mode,omitempty" yaml:"mode,omitempty"` Size matcher `json:"size,omitempty" yaml:"size,omitempty"` @@ -27,15 +28,26 @@ type File struct { Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (f *File) ID() string { return f.Path } -func (f *File) SetID(id string) { f.Path = id } +func (f *File) ID() string { + if f.Path != "" && f.Path != f.id { + return fmt.Sprintf("%s: %s", f.id, f.Path) + } + return f.id +} +func (f *File) SetID(id string) { f.id = id } func (f *File) GetTitle() string { return f.Title } func (f *File) GetMeta() meta { return f.Meta } +func (f *File) GetPath() string { + if f.Path != "" { + return f.Path + } + return f.id +} func (f *File) Validate(sys *system.System) []TestResult { skip := false - sysFile := sys.NewFile(f.Path, sys, util.Config{}) + sysFile := sys.NewFile(f.GetPath(), sys, util.Config{}) if f.Skip { skip = true @@ -90,7 +102,7 @@ func NewFile(sysFile system.File, config util.Config) (*File, error) { return nil, err } f := &File{ - Path: path, + id: path, Exists: exists, Contents: []string{}, } diff --git a/resource/group.go b/resource/group.go index 703610da4..f23cb39a8 100644 --- a/resource/group.go +++ b/resource/group.go @@ -10,21 +10,33 @@ import ( type Group struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Groupname string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Groupname string `json:"groupname,omitempty" yaml:"groupname,omitempty"` Exists matcher `json:"exists" yaml:"exists"` GID matcher `json:"gid,omitempty" yaml:"gid,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (g *Group) ID() string { return g.Groupname } -func (g *Group) SetID(id string) { g.Groupname = id } +func (g *Group) ID() string { + if g.Groupname != "" && g.Groupname != g.id { + return fmt.Sprintf("%s: %s", g.id, g.Groupname) + } + return g.id +} +func (g *Group) SetID(id string) { g.id = id } func (g *Group) GetTitle() string { return g.Title } func (g *Group) GetMeta() meta { return g.Meta } +func (g *Group) GetGroupname() string { + if g.Groupname != "" { + return g.Groupname + } + return g.id +} func (g *Group) Validate(sys *system.System) []TestResult { skip := false - sysgroup := sys.NewGroup(g.Groupname, sys, util.Config{}) + sysgroup := sys.NewGroup(g.GetGroupname(), sys, util.Config{}) if g.Skip { skip = true @@ -36,7 +48,7 @@ func (g *Group) Validate(sys *system.System) []TestResult { skip = true } if g.GID != nil { - gGID := deprecateAtoI(g.GID, fmt.Sprintf("%s: group.gid", g.Groupname)) + gGID := deprecateAtoI(g.GID, fmt.Sprintf("%s: group.gid", g.ID())) results = append(results, ValidateValue(g, "gid", gGID, sysgroup.GID, skip)) } return results @@ -46,8 +58,8 @@ func NewGroup(sysGroup system.Group, config util.Config) (*Group, error) { groupname := sysGroup.Groupname() exists, _ := sysGroup.Exists() g := &Group{ - Groupname: groupname, - Exists: exists, + id: groupname, + Exists: exists, } if !contains(config.IgnoreList, "stderr") { if gid, err := sysGroup.GID(); err == nil { diff --git a/resource/http.go b/resource/http.go index c82896217..08cfc6c3c 100644 --- a/resource/http.go +++ b/resource/http.go @@ -1,6 +1,7 @@ package resource import ( + "fmt" "time" "github.com/aelsabbahy/goss/system" @@ -9,9 +10,9 @@ import ( type HTTP struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` - URL string `json:"url,omitempty" yaml:"url,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - HTTP string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + URL string `json:"url,omitempty" yaml:"url,omitempty"` Method string `json:"method,omitempty" yaml:"method,omitempty"` Status matcher `json:"status" yaml:"status"` AllowInsecure bool `json:"allow-insecure" yaml:"allow-insecure"` @@ -27,19 +28,23 @@ type HTTP struct { Proxy string `json:"proxy,omitempty" yaml:"proxy,omitempty"` } -func (u *HTTP) ID() string { return u.HTTP } +func (h *HTTP) ID() string { + if h.URL != "" && h.URL != h.id { + return fmt.Sprintf("%s: %s", h.id, h.URL) + } + return h.id +} -func (u *HTTP) SetID(id string) { u.HTTP = id } +func (h *HTTP) SetID(id string) { h.id = id } // FIXME: Can this be refactored? func (r *HTTP) GetTitle() string { return r.Title } func (r *HTTP) GetMeta() meta { return r.Meta } - func (r *HTTP) getURL() string { if r.URL != "" { return r.URL } - return r.HTTP + return r.id } func (u *HTTP) Validate(sys *system.System) []TestResult { @@ -77,7 +82,7 @@ func NewHTTP(sysHTTP system.HTTP, config util.Config) (*HTTP, error) { http := sysHTTP.HTTP() status, err := sysHTTP.Status() u := &HTTP{ - HTTP: http, + id: http, Status: status, RequestHeader: []string{}, Headers: nil, diff --git a/resource/interface.go b/resource/interface.go index bd93a0f92..7312b5365 100644 --- a/resource/interface.go +++ b/resource/interface.go @@ -1,6 +1,8 @@ package resource import ( + "fmt" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) @@ -8,23 +10,35 @@ import ( type Interface struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Name string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` Exists matcher `json:"exists" yaml:"exists"` Addrs matcher `json:"addrs,omitempty" yaml:"addrs,omitempty"` MTU matcher `json:"mtu,omitempty" yaml:"mtu,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (i *Interface) ID() string { return i.Name } -func (i *Interface) SetID(id string) { i.Name = id } +func (i *Interface) ID() string { + if i.Name != "" && i.Name != i.id { + return fmt.Sprintf("%s: %s", i.id, i.Name) + } + return i.id +} +func (i *Interface) SetID(id string) { i.id = id } // FIXME: Can this be refactored? func (i *Interface) GetTitle() string { return i.Title } func (i *Interface) GetMeta() meta { return i.Meta } +func (i *Interface) GetName() string { + if i.Name != "" { + return i.Name + } + return i.id +} func (i *Interface) Validate(sys *system.System) []TestResult { skip := false - sysInterface := sys.NewInterface(i.Name, sys, util.Config{}) + sysInterface := sys.NewInterface(i.GetName(), sys, util.Config{}) if i.Skip { skip = true @@ -48,7 +62,7 @@ func NewInterface(sysInterface system.Interface, config util.Config) (*Interface name := sysInterface.Name() exists, _ := sysInterface.Exists() i := &Interface{ - Name: name, + id: name, Exists: exists, } if !contains(config.IgnoreList, "addrs") { diff --git a/resource/kernel_param.go b/resource/kernel_param.go index 7e82c9caf..5c1ad7ab8 100644 --- a/resource/kernel_param.go +++ b/resource/kernel_param.go @@ -1,6 +1,8 @@ package resource import ( + "fmt" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) @@ -8,23 +10,36 @@ import ( type KernelParam struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + id string `json:"-" yaml:"-"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` Key string `json:"-" yaml:"-"` Value matcher `json:"value" yaml:"value"` } -func (a *KernelParam) ID() string { return a.Key } -func (a *KernelParam) SetID(id string) { a.Key = id } +func (k *KernelParam) ID() string { + if k.Name != "" && k.Name != k.id { + return fmt.Sprintf("%s: %s", k.id, k.Name) + } + return k.id +} +func (a *KernelParam) SetID(id string) { a.id = id } // FIXME: Can this be refactored? -func (r *KernelParam) GetTitle() string { return r.Title } -func (r *KernelParam) GetMeta() meta { return r.Meta } +func (k *KernelParam) GetTitle() string { return k.Title } +func (k *KernelParam) GetMeta() meta { return k.Meta } +func (k *KernelParam) GetName() string { + if k.Name != "" { + return k.Name + } + return k.id +} -func (a *KernelParam) Validate(sys *system.System) []TestResult { +func (k *KernelParam) Validate(sys *system.System) []TestResult { skip := false - sysKernelParam := sys.NewKernelParam(a.Key, sys, util.Config{}) + sysKernelParam := sys.NewKernelParam(k.GetName(), sys, util.Config{}) var results []TestResult - results = append(results, ValidateValue(a, "value", a.Value, sysKernelParam.Value, skip)) + results = append(results, ValidateValue(k, "value", k.Value, sysKernelParam.Value, skip)) return results } @@ -32,7 +47,7 @@ func NewKernelParam(sysKernelParam system.KernelParam, config util.Config) (*Ker key := sysKernelParam.Key() value, err := sysKernelParam.Value() a := &KernelParam{ - Key: key, + id: key, Value: value, } return a, err diff --git a/resource/matching.go b/resource/matching.go index 040fc3f56..05346bb97 100644 --- a/resource/matching.go +++ b/resource/matching.go @@ -16,14 +16,14 @@ type Matching struct { Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` Content interface{} `json:"content,omitempty" yaml:"content,omitempty"` AsReader bool `json:"as-reader,omitempty" yaml:"as-reader,omitempty"` - Id string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` Matches matcher `json:"matches" yaml:"matches"` } type MatchingMap map[string]*Matching -func (a *Matching) ID() string { return a.Id } -func (a *Matching) SetID(id string) { a.Id = id } +func (a *Matching) ID() string { return a.id } +func (a *Matching) SetID(id string) { a.id = id } // FIXME: Can this be refactored? func (r *Matching) GetTitle() string { return r.Title } diff --git a/resource/mount.go b/resource/mount.go index 2e790d3f8..08b0899ac 100644 --- a/resource/mount.go +++ b/resource/mount.go @@ -1,6 +1,8 @@ package resource import ( + "fmt" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) @@ -8,7 +10,8 @@ import ( type Mount struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - MountPoint string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + MountPoint string `json:"mountpoint,omitempty" yaml:"mountpoint,omitempty"` Exists matcher `json:"exists" yaml:"exists"` Opts matcher `json:"opts,omitempty" yaml:"opts,omitempty"` Source matcher `json:"source,omitempty" yaml:"source,omitempty"` @@ -17,16 +20,27 @@ type Mount struct { Usage matcher `json:"usage,omitempty" yaml:"usage,omitempty"` } -func (m *Mount) ID() string { return m.MountPoint } -func (m *Mount) SetID(id string) { m.MountPoint = id } +func (m *Mount) ID() string { + if m.MountPoint != "" && m.MountPoint != m.id { + return fmt.Sprintf("%s: %s", m.id, m.MountPoint) + } + return m.id +} +func (m *Mount) SetID(id string) { m.id = id } // FIXME: Can this be refactored? func (m *Mount) GetTitle() string { return m.Title } func (m *Mount) GetMeta() meta { return m.Meta } +func (m *Mount) GetMountPoint() string { + if m.MountPoint != "" { + return m.MountPoint + } + return m.id +} func (m *Mount) Validate(sys *system.System) []TestResult { skip := false - sysMount := sys.NewMount(m.MountPoint, sys, util.Config{}) + sysMount := sys.NewMount(m.GetMountPoint(), sys, util.Config{}) if m.Skip { skip = true @@ -56,8 +70,8 @@ func NewMount(sysMount system.Mount, config util.Config) (*Mount, error) { mountPoint := sysMount.MountPoint() exists, _ := sysMount.Exists() m := &Mount{ - MountPoint: mountPoint, - Exists: exists, + id: mountPoint, + Exists: exists, } if !contains(config.IgnoreList, "opts") { if opts, err := sysMount.Opts(); err == nil { diff --git a/resource/package.go b/resource/package.go index b70fbb063..a07c857d5 100644 --- a/resource/package.go +++ b/resource/package.go @@ -1,6 +1,8 @@ package resource import ( + "fmt" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) @@ -8,21 +10,33 @@ import ( type Package struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Name string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` Installed matcher `json:"installed" yaml:"installed"` Versions matcher `json:"versions,omitempty" yaml:"versions,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (p *Package) ID() string { return p.Name } -func (p *Package) SetID(id string) { p.Name = id } +func (p *Package) ID() string { + if p.Name != "" && p.Name != p.id { + return fmt.Sprintf("%s: %s", p.id, p.Name) + } + return p.id +} +func (p *Package) SetID(id string) { p.id = id } func (p *Package) GetTitle() string { return p.Title } func (p *Package) GetMeta() meta { return p.Meta } +func (p *Package) GetName() string { + if p.Name != "" { + return p.Name + } + return p.id +} func (p *Package) Validate(sys *system.System) []TestResult { skip := false - sysPkg := sys.NewPackage(p.Name, sys, util.Config{}) + sysPkg := sys.NewPackage(p.GetName(), sys, util.Config{}) if p.Skip { skip = true @@ -43,7 +57,7 @@ func NewPackage(sysPackage system.Package, config util.Config) (*Package, error) name := sysPackage.Name() installed, _ := sysPackage.Installed() p := &Package{ - Name: name, + id: name, Installed: installed, } if !contains(config.IgnoreList, "versions") { diff --git a/resource/port.go b/resource/port.go index 8753035dc..5e75a4bcc 100644 --- a/resource/port.go +++ b/resource/port.go @@ -1,6 +1,8 @@ package resource import ( + "fmt" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) @@ -8,21 +10,33 @@ import ( type Port struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Port string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Port string `json:"port,omitempty" yaml:"port,omitempty"` Listening matcher `json:"listening" yaml:"listening"` IP matcher `json:"ip,omitempty" yaml:"ip,omitempty"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (p *Port) ID() string { return p.Port } -func (p *Port) SetID(id string) { p.Port = id } +func (p *Port) ID() string { + if p.Port != "" && p.Port != p.id { + return fmt.Sprintf("%s: %s", p.id, p.Port) + } + return p.id +} +func (p *Port) SetID(id string) { p.id = id } func (p *Port) GetTitle() string { return p.Title } func (p *Port) GetMeta() meta { return p.Meta } +func (p *Port) GetPort() string { + if p.Port != "" { + return p.Port + } + return p.id +} func (p *Port) Validate(sys *system.System) []TestResult { skip := false - sysPort := sys.NewPort(p.Port, sys, util.Config{}) + sysPort := sys.NewPort(p.GetPort(), sys, util.Config{}) if p.Skip { skip = true @@ -43,7 +57,7 @@ func NewPort(sysPort system.Port, config util.Config) (*Port, error) { port := sysPort.Port() listening, _ := sysPort.Listening() p := &Port{ - Port: port, + id: port, Listening: listening, } if !contains(config.IgnoreList, "ip") { diff --git a/resource/process.go b/resource/process.go index a6cafb9f3..6cef8b065 100644 --- a/resource/process.go +++ b/resource/process.go @@ -1,27 +1,41 @@ package resource import ( + "fmt" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) type Process struct { - Title string `json:"title,omitempty" yaml:"title,omitempty"` - Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Executable string `json:"-" yaml:"-"` - Running matcher `json:"running" yaml:"running"` - Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` + Title string `json:"title,omitempty" yaml:"title,omitempty"` + Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` + id string `json:"-" yaml:"-"` + Comm string `json:"comm,omitempty" yaml:"comm,omitempty"` + Running matcher `json:"running" yaml:"running"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (p *Process) ID() string { return p.Executable } -func (p *Process) SetID(id string) { p.Executable = id } +func (p *Process) ID() string { + if p.Comm != "" && p.Comm != p.id { + return fmt.Sprintf("%s: %s", p.id, p.Comm) + } + return p.id +} +func (p *Process) SetID(id string) { p.id = id } func (p *Process) GetTitle() string { return p.Title } func (p *Process) GetMeta() meta { return p.Meta } +func (p *Process) GetComm() string { + if p.Comm != "" { + return p.Comm + } + return p.id +} func (p *Process) Validate(sys *system.System) []TestResult { skip := false - sysProcess := sys.NewProcess(p.Executable, sys, util.Config{}) + sysProcess := sys.NewProcess(p.GetComm(), sys, util.Config{}) if p.Skip { skip = true @@ -39,7 +53,7 @@ func NewProcess(sysProcess system.Process, config util.Config) (*Process, error) return nil, err } return &Process{ - Executable: executable, - Running: running, + id: executable, + Running: running, }, nil } diff --git a/resource/service.go b/resource/service.go index d8326f8fb..d8da5d467 100644 --- a/resource/service.go +++ b/resource/service.go @@ -1,6 +1,8 @@ package resource import ( + "fmt" + "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" ) @@ -8,22 +10,34 @@ import ( type Service struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Service string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Name string `json:"name,omitempty" yaml:"name,omitempty"` Enabled matcher `json:"enabled" yaml:"enabled"` Running matcher `json:"running" yaml:"running"` Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` RunLevels matcher `json:"runlevels,omitempty" yaml:"runlevels,omitempty"` } -func (s *Service) ID() string { return s.Service } -func (s *Service) SetID(id string) { s.Service = id } +func (s *Service) ID() string { + if s.Name != "" && s.Name != s.id { + return fmt.Sprintf("%s: %s", s.id, s.Name) + } + return s.id +} +func (s *Service) SetID(id string) { s.id = id } func (s *Service) GetTitle() string { return s.Title } func (s *Service) GetMeta() meta { return s.Meta } +func (s *Service) GetName() string { + if s.Name != "" { + return s.Name + } + return s.id +} func (s *Service) Validate(sys *system.System) []TestResult { skip := false - sysservice := sys.NewService(s.Service, sys, util.Config{}) + sysservice := sys.NewService(s.GetName(), sys, util.Config{}) if s.Skip { skip = true @@ -53,7 +67,7 @@ func NewService(sysService system.Service, config util.Config) (*Service, error) return nil, err } return &Service{ - Service: service, + id: service, Enabled: enabled, Running: running, }, nil diff --git a/resource/user.go b/resource/user.go index c4f85cd92..dacd83589 100644 --- a/resource/user.go +++ b/resource/user.go @@ -10,7 +10,8 @@ import ( type User struct { Title string `json:"title,omitempty" yaml:"title,omitempty"` Meta meta `json:"meta,omitempty" yaml:"meta,omitempty"` - Username string `json:"-" yaml:"-"` + id string `json:"-" yaml:"-"` + Username string `json:"username,omitempty" yaml:"username,omitempty"` Exists matcher `json:"exists" yaml:"exists"` UID matcher `json:"uid,omitempty" yaml:"uid,omitempty"` GID matcher `json:"gid,omitempty" yaml:"gid,omitempty"` @@ -20,15 +21,26 @@ type User struct { Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } -func (u *User) ID() string { return u.Username } -func (u *User) SetID(id string) { u.Username = id } +func (u *User) ID() string { + if u.Username != "" && u.Username != u.id { + return fmt.Sprintf("%s: %s", u.id, u.Username) + } + return u.id +} +func (u *User) SetID(id string) { u.id = id } func (u *User) GetTitle() string { return u.Title } func (u *User) GetMeta() meta { return u.Meta } +func (u *User) GetUsername() string { + if u.Username != "" { + return u.Username + } + return u.id +} func (u *User) Validate(sys *system.System) []TestResult { skip := false - sysuser := sys.NewUser(u.Username, sys, util.Config{}) + sysuser := sys.NewUser(u.GetUsername(), sys, util.Config{}) if u.Skip { skip = true @@ -63,8 +75,8 @@ func NewUser(sysUser system.User, config util.Config) (*User, error) { username := sysUser.Username() exists, _ := sysUser.Exists() u := &User{ - Username: username, - Exists: exists, + id: username, + Exists: exists, } if !contains(config.IgnoreList, "uid") { if uid, err := sysUser.UID(); err == nil { From 61922e68ff471cddfec55cbbb4f1b333a43258f8 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 6 Oct 2022 17:14:22 -0700 Subject: [PATCH 18/41] Sort output in documentation format closes #416 --- docs/manual.md | 5 +++-- outputs/bench.go | 8 +++++++- outputs/documentation.go | 7 ++++++- outputs/json.go | 4 ++++ outputs/junit.go | 7 ++++++- outputs/outputs.go | 29 +++++++++++++++++++++++++++++ outputs/structured.go | 5 +++++ outputs/tap.go | 7 ++++++- resource/validate.go | 4 ++++ 9 files changed, 70 insertions(+), 6 deletions(-) diff --git a/docs/manual.md b/docs/manual.md index 8c678688e..e5bb74a60 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -293,8 +293,9 @@ The `application/vnd.goss-{output format}` media type can be used in the `Accept * `silent` - No output. Avoids exposing system information (e.g. when serving tests as a healthcheck endpoint) * `--format-options`, `-o` (output format option) * `perfdata` - Outputs Nagios "performance data". Applies to `nagios` output - * `verbose` - Gives verbose output. Applies to `nagios` output - * `pretty` - Pretty printing for the `json` output + * `verbose` - Gives verbose output. Applies to `nagios` output + * `pretty` - Pretty printing for the `json` output + * `sort` - Sorts the results * `--max-concurrent` - Max number of tests to run concurrently * `--no-color` - Disable color * `--color` - Force enable color diff --git a/outputs/bench.go b/outputs/bench.go index 1777cd5e3..fd5ef3af1 100644 --- a/outputs/bench.go +++ b/outputs/bench.go @@ -12,11 +12,17 @@ import ( type Bench struct{} func (r Bench) ValidOptions() []*formatOption { - return []*formatOption{} + return []*formatOption{ + {name: foSort}, + } } func (r Bench) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + + sort := util.IsValueInList(foSort, outConfig.FormatOptions) + results = getResults(results, sort) + var testCount, skipped, failed int for resultGroup := range results { for _, testResult := range resultGroup { diff --git a/outputs/documentation.go b/outputs/documentation.go index 8a3e550a5..38f36268e 100644 --- a/outputs/documentation.go +++ b/outputs/documentation.go @@ -12,13 +12,18 @@ import ( type Documentation struct{} func (r Documentation) ValidOptions() []*formatOption { - return []*formatOption{} + return []*formatOption{ + {name: foSort}, + } } func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + sort := util.IsValueInList(foSort, outConfig.FormatOptions) + results = getResults(results, sort) + testCount := 0 var failedOrSkipped [][]resource.TestResult var skipped, failed int diff --git a/outputs/json.go b/outputs/json.go index d1fb4438a..3fa7c0ad4 100644 --- a/outputs/json.go +++ b/outputs/json.go @@ -16,6 +16,7 @@ type Json struct{} func (r Json) ValidOptions() []*formatOption { return []*formatOption{ {name: foPretty}, + {name: foSort}, } } @@ -26,6 +27,9 @@ func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, pretty = util.IsValueInList(foPretty, outConfig.FormatOptions) includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + sort := util.IsValueInList(foSort, outConfig.FormatOptions) + results = getResults(results, sort) + color.NoColor = true testCount := 0 failed := 0 diff --git a/outputs/junit.go b/outputs/junit.go index 44dc4b58e..9ca227e54 100644 --- a/outputs/junit.go +++ b/outputs/junit.go @@ -16,13 +16,18 @@ import ( type JUnit struct{} func (r JUnit) ValidOptions() []*formatOption { - return []*formatOption{} + return []*formatOption{ + {name: foSort}, + } } func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + sort := util.IsValueInList(foSort, outConfig.FormatOptions) + results = getResults(results, sort) + color.NoColor = true var testCount, failed, skipped int diff --git a/outputs/outputs.go b/outputs/outputs.go index 139410111..5d97530d4 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -45,6 +45,7 @@ var ( foVerbose = "verbose" foPretty = "pretty" foIncludeRaw = "include_raw" + foSort = "sort" ) var green = color.New(color.FgGreen).SprintfFunc() @@ -243,6 +244,9 @@ func failedOrSkippedSummary(failedOrSkipped [][]resource.TestResult, includeRaw var s string if len(failedOrSkipped) > 0 { s += fmt.Sprint("Failures/Skipped:\n\n") + sort.Slice(failedOrSkipped, func(i, j int) bool { + return failedOrSkipped[i][0].SortKey() < failedOrSkipped[j][0].SortKey() + }) for _, failedGroup := range failedOrSkipped { first := failedGroup[0] header := header(first) @@ -257,3 +261,28 @@ func failedOrSkippedSummary(failedOrSkipped [][]resource.TestResult, includeRaw } return s } + +func getResults(tr <-chan []resource.TestResult, doSort bool) <-chan []resource.TestResult { + if !doSort { + return tr + } + str := make([][]resource.TestResult, 0) + for i := range tr { + str = append(str, i) + } + + sort.Slice(str, func(i, j int) bool { + return str[i][0].SortKey() < str[j][0].SortKey() + }) + + c := make(chan []resource.TestResult) + go func(c chan []resource.TestResult) { + defer close(c) + + for _, i := range str { + c <- i + } + }(c) + + return c +} diff --git a/outputs/structured.go b/outputs/structured.go index dad49c70e..7e0a4e6d2 100644 --- a/outputs/structured.go +++ b/outputs/structured.go @@ -16,6 +16,7 @@ type Structured struct{} func (r Structured) ValidOptions() []*formatOption { return []*formatOption{ {name: foPretty}, + {name: foSort}, } } @@ -48,6 +49,10 @@ func (s *StructureTestSummary) String() string { // Output processes output from tests into StructuredOutput written to w as a string func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + + sort := util.IsValueInList(foSort, outConfig.FormatOptions) + results = getResults(results, sort) + result := &StructuredOutput{ Results: []StructuredTestResult{}, Summary: StructureTestSummary{}, diff --git a/outputs/tap.go b/outputs/tap.go index 7530e87f4..296b6f3e1 100644 --- a/outputs/tap.go +++ b/outputs/tap.go @@ -13,13 +13,18 @@ import ( type Tap struct{} func (r Tap) ValidOptions() []*formatOption { - return []*formatOption{} + return []*formatOption{ + {name: foSort}, + } } func (r Tap) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + sort := util.IsValueInList(foSort, outConfig.FormatOptions) + results = getResults(results, sort) + testCount := 0 failed := 0 diff --git a/resource/validate.go b/resource/validate.go index ebe068ca9..974858869 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -43,6 +43,10 @@ type TestResult struct { Duration time.Duration `json:"duration" yaml:"duration"` } +func (t TestResult) SortKey() string { + return fmt.Sprintf("%s:%s", t.ResourceType, t.ResourceId) +} + func skipResult(typeS string, id string, title string, meta meta, property string, startTime time.Time) TestResult { return TestResult{ Result: SKIP, From 14c3d45495b487f150d87e52021bb9ae6adfe6c9 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 17 Nov 2022 07:08:01 -0800 Subject: [PATCH 19/41] Fix typo in contain_element message --- matchers/contain_element_matcher.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matchers/contain_element_matcher.go b/matchers/contain_element_matcher.go index a3fc7c5c2..a0c4dd7ae 100644 --- a/matchers/contain_element_matcher.go +++ b/matchers/contain_element_matcher.go @@ -29,7 +29,7 @@ func (m *ContainElementMatcher) FailureResult(actual interface{}) MatcherResult func (m *ContainElementMatcher) NegatedFailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: "to contain element matching", + Message: "to not contain element matching", Expected: m.Element, } } From db012e47f0593f93de2def3e911da9ac3fec41ad Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 18 Feb 2023 08:30:47 -0800 Subject: [PATCH 20/41] Track start and end times per-test This also changes the way total time is calculated in the output summary. total time = endTime of last test - start time of first test --- cmd/goss/goss.go | 3 +-- outputs/bench.go | 12 ++++++++++-- outputs/documentation.go | 12 ++++++++++-- outputs/json.go | 12 ++++++++++-- outputs/junit.go | 12 ++++++++++-- outputs/nagios.go | 12 ++++++++++-- outputs/outputs.go | 6 +++--- outputs/rspecish.go | 12 ++++++++++-- outputs/silent.go | 3 +-- outputs/structured.go | 12 ++++++++++-- outputs/tap.go | 3 +-- resource/validate.go | 17 ++++++++++++++--- serve.go | 13 +------------ validate.go | 6 +++--- 14 files changed, 94 insertions(+), 41 deletions(-) diff --git a/cmd/goss/goss.go b/cmd/goss/goss.go index 02b326077..964099c95 100644 --- a/cmd/goss/goss.go +++ b/cmd/goss/goss.go @@ -65,7 +65,6 @@ func timeoutFlag(value time.Duration) cli.DurationFlag { } func main() { - startTime := time.Now() app := cli.NewApp() app.EnableBashCompletion = true app.Version = version @@ -141,7 +140,7 @@ func main() { }, Action: func(c *cli.Context) error { fatalAlphaIfNeeded(c) - code, err := goss.Validate(newRuntimeConfigFromCLI(c), startTime) + code, err := goss.Validate(newRuntimeConfigFromCLI(c)) if err != nil { color.Red(fmt.Sprintf("Error: %v\n", err)) } diff --git a/outputs/bench.go b/outputs/bench.go index fd5ef3af1..df2298805 100644 --- a/outputs/bench.go +++ b/outputs/bench.go @@ -17,15 +17,23 @@ func (r Bench) ValidOptions() []*formatOption { } } -func (r Bench) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { +func (r Bench) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) + var startTime time.Time + var endTime time.Time var testCount, skipped, failed int for resultGroup := range results { for _, testResult := range resultGroup { + if startTime.IsZero() || testResult.StartTime.Before(startTime) { + startTime = testResult.StartTime + } + if endTime.IsZero() || testResult.EndTime.After(endTime) { + endTime = testResult.EndTime + } fmt.Fprintf(w, "%v %s\n", testResult.Duration, humanizeResult(testResult, true, includeRaw)) switch testResult.Result { case resource.SKIP: @@ -37,7 +45,7 @@ func (r Bench) Output(w io.Writer, results <-chan []resource.TestResult, startTi } } - fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) + fmt.Fprint(w, summary(startTime, endTime, testCount, failed, skipped)) if failed > 0 { return 1 } diff --git a/outputs/documentation.go b/outputs/documentation.go index 38f36268e..7a209171c 100644 --- a/outputs/documentation.go +++ b/outputs/documentation.go @@ -18,12 +18,14 @@ func (r Documentation) ValidOptions() []*formatOption { } func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) + var startTime time.Time + var endTime time.Time testCount := 0 var failedOrSkipped [][]resource.TestResult var skipped, failed int @@ -35,6 +37,12 @@ func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, fmt.Fprint(w, header) } for _, testResult := range resultGroup { + if startTime.IsZero() || testResult.StartTime.Before(startTime) { + startTime = testResult.StartTime + } + if endTime.IsZero() || testResult.EndTime.After(endTime) { + endTime = testResult.EndTime + } switch testResult.Result { case resource.SUCCESS: fmt.Fprintln(w, humanizeResult(testResult, false, includeRaw)) @@ -57,7 +65,7 @@ func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, fmt.Fprint(w, "\n\n") fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped, includeRaw)) - fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) + fmt.Fprint(w, summary(startTime, endTime, testCount, failed, skipped)) if failed > 0 { return 1 } diff --git a/outputs/json.go b/outputs/json.go index 3fa7c0ad4..6cdc22375 100644 --- a/outputs/json.go +++ b/outputs/json.go @@ -21,7 +21,7 @@ func (r Json) ValidOptions() []*formatOption { } func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { var pretty bool pretty = util.IsValueInList(foPretty, outConfig.FormatOptions) @@ -30,12 +30,20 @@ func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) + var startTime time.Time + var endTime time.Time color.NoColor = true testCount := 0 failed := 0 var resultsOut []map[string]interface{} for resultGroup := range results { for _, testResult := range resultGroup { + if startTime.IsZero() || testResult.StartTime.Before(startTime) { + startTime = testResult.StartTime + } + if endTime.IsZero() || testResult.EndTime.After(endTime) { + endTime = testResult.EndTime + } if testResult.Result == resource.FAIL { failed++ } @@ -49,7 +57,7 @@ func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, } summary := make(map[string]interface{}) - duration := time.Since(startTime) + duration := endTime.Sub(startTime) summary["test-count"] = testCount summary["failed-count"] = failed summary["total-duration"] = duration diff --git a/outputs/junit.go b/outputs/junit.go index 9ca227e54..5a79135fd 100644 --- a/outputs/junit.go +++ b/outputs/junit.go @@ -22,7 +22,7 @@ func (r JUnit) ValidOptions() []*formatOption { } func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) @@ -37,8 +37,16 @@ func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, var summary map[int]string summary = make(map[int]string) + var startTime time.Time + var endTime time.Time for resultGroup := range results { for _, testResult := range resultGroup { + if startTime.IsZero() || testResult.StartTime.Before(startTime) { + startTime = testResult.StartTime + } + if endTime.IsZero() || testResult.EndTime.After(endTime) { + endTime = testResult.EndTime + } m := struct2map(testResult) duration := strconv.FormatFloat(m["duration"].(float64)/1000/1000/1000, 'f', 3, 64) summary[testCount] = "") fmt.Fprintf(w, "\n", diff --git a/outputs/nagios.go b/outputs/nagios.go index 09ce02f36..0bff6d213 100644 --- a/outputs/nagios.go +++ b/outputs/nagios.go @@ -20,7 +20,7 @@ func (r Nagios) ValidOptions() []*formatOption { } func (r Nagios) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { var testCount, failed, skipped int @@ -29,11 +29,19 @@ func (r Nagios) Output(w io.Writer, results <-chan []resource.TestResult, verbose = util.IsValueInList(foVerbose, outConfig.FormatOptions) includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + var startTime time.Time + var endTime time.Time var summary map[int]string summary = make(map[int]string) for resultGroup := range results { for _, testResult := range resultGroup { + if startTime.IsZero() || testResult.StartTime.Before(startTime) { + startTime = testResult.StartTime + } + if endTime.IsZero() || testResult.EndTime.After(endTime) { + endTime = testResult.EndTime + } switch testResult.Result { case resource.FAIL: if util.IsValueInList(foVerbose, outConfig.FormatOptions) { @@ -47,7 +55,7 @@ func (r Nagios) Output(w io.Writer, results <-chan []resource.TestResult, } } - duration := time.Since(startTime) + duration := endTime.Sub(startTime) if failed > 0 { fmt.Fprintf(w, "GOSS CRITICAL - Count: %d, Failed: %d, Skipped: %d, Duration: %.3fs", testCount, failed, skipped, duration.Seconds()) if perfdata { diff --git a/outputs/outputs.go b/outputs/outputs.go index 5d97530d4..db152ff18 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -24,7 +24,7 @@ type formatOption struct { } type Outputer interface { - Output(io.Writer, <-chan []resource.TestResult, time.Time, util.OutputConfig) int + Output(io.Writer, <-chan []resource.TestResult, util.OutputConfig) int ValidOptions() []*formatOption } @@ -229,9 +229,9 @@ func header(t resource.TestResult) string { return out } -func summary(startTime time.Time, count, failed, skipped int) string { +func summary(startTime, endTime time.Time, 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", endTime.Sub(startTime).Seconds()) f := green if failed > 0 { f = red diff --git a/outputs/rspecish.go b/outputs/rspecish.go index 355bf48c9..f527716c5 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -16,14 +16,22 @@ func (r Rspecish) ValidOptions() []*formatOption { } func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { + var startTime time.Time + var endTime time.Time testCount := 0 var failedOrSkipped [][]resource.TestResult var skipped, failed int for resultGroup := range results { failedOrSkippedGroup := []resource.TestResult{} for _, testResult := range resultGroup { + if startTime.IsZero() || testResult.StartTime.Before(startTime) { + startTime = testResult.StartTime + } + if endTime.IsZero() || testResult.EndTime.After(endTime) { + endTime = testResult.EndTime + } switch testResult.Result { case resource.SUCCESS: fmt.Fprintf(w, green(".")) @@ -48,7 +56,7 @@ func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped, includeRaw)) - fmt.Fprint(w, summary(startTime, testCount, failed, skipped)) + fmt.Fprint(w, summary(startTime, endTime, testCount, failed, skipped)) if failed > 0 { return 1 } diff --git a/outputs/silent.go b/outputs/silent.go index 638c8bbb0..c4f5d9e95 100644 --- a/outputs/silent.go +++ b/outputs/silent.go @@ -2,7 +2,6 @@ package outputs import ( "io" - "time" "github.com/aelsabbahy/goss/resource" "github.com/aelsabbahy/goss/util" @@ -15,7 +14,7 @@ func (r Silent) ValidOptions() []*formatOption { } func (r Silent) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { var failed int for resultGroup := range results { diff --git a/outputs/structured.go b/outputs/structured.go index 7e0a4e6d2..3bebd18b9 100644 --- a/outputs/structured.go +++ b/outputs/structured.go @@ -47,7 +47,7 @@ func (s *StructureTestSummary) String() string { } // Output processes output from tests into StructuredOutput written to w as a string -func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, startTime time.Time, outConfig util.OutputConfig) (exitCode int) { +func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) @@ -58,8 +58,16 @@ func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, st Summary: StructureTestSummary{}, } + var startTime time.Time + var endTime time.Time for resultGroup := range results { for _, testResult := range resultGroup { + if startTime.IsZero() || testResult.StartTime.Before(startTime) { + startTime = testResult.StartTime + } + if endTime.IsZero() || testResult.EndTime.After(endTime) { + endTime = testResult.EndTime + } r := StructuredTestResult{ TestResult: testResult, SummaryLine: humanizeResult(testResult, false, includeRaw), @@ -76,7 +84,7 @@ func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, st } } - result.Summary.TotalDuration = time.Since(startTime) + result.Summary.TotalDuration = endTime.Sub(startTime) result.SummaryLine = result.Summary.String() var j []byte diff --git a/outputs/tap.go b/outputs/tap.go index 296b6f3e1..c1abdd74f 100644 --- a/outputs/tap.go +++ b/outputs/tap.go @@ -4,7 +4,6 @@ import ( "fmt" "io" "strconv" - "time" "github.com/aelsabbahy/goss/resource" "github.com/aelsabbahy/goss/util" @@ -19,7 +18,7 @@ func (r Tap) ValidOptions() []*formatOption { } func (r Tap) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) diff --git a/resource/validate.go b/resource/validate.go index 974858869..30ee5c621 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -40,6 +40,8 @@ type TestResult struct { Result string `json:"result" yaml:"result"` Err error `json:"err" yaml:"err"` MatcherResult matchers.MatcherResult `json:"matcher-result" yaml:"matcher-result"` + StartTime time.Time `json:"duration" yaml:"duration"` + EndTime time.Time `json:"duration" yaml:"duration"` Duration time.Duration `json:"duration" yaml:"duration"` } @@ -48,6 +50,7 @@ func (t TestResult) SortKey() string { } func skipResult(typeS string, id string, title string, meta meta, property string, startTime time.Time) TestResult { + endTime := time.Now() return TestResult{ Result: SKIP, ResourceType: typeS, @@ -55,7 +58,9 @@ func skipResult(typeS string, id string, title string, meta meta, property strin Title: title, Meta: meta, Property: property, - Duration: startTime.Sub(startTime), + StartTime: startTime, + EndTime: endTime, + Duration: endTime.Sub(startTime), } } @@ -124,6 +129,7 @@ func ValidateGomegaValue(res ResourceRead, property string, expectedValue interf gomegaMatcher, err = matcherToGomegaMatcher(expectedValue) } if err != nil { + endTime := time.Now() return TestResult{ Result: FAIL, ResourceType: typeS, @@ -132,7 +138,9 @@ func ValidateGomegaValue(res ResourceRead, property string, expectedValue interf Meta: meta, Property: property, Err: err, - Duration: time.Now().Sub(startTime), + StartTime: startTime, + EndTime: endTime, + Duration: endTime.Sub(startTime), } } @@ -151,6 +159,7 @@ func ValidateGomegaValue(res ResourceRead, property string, expectedValue interf result = FAIL } + endTime := time.Now() return TestResult{ Result: result, ResourceType: typeS, @@ -160,6 +169,8 @@ func ValidateGomegaValue(res ResourceRead, property string, expectedValue interf Property: property, MatcherResult: matcherResult, Err: err, - Duration: time.Now().Sub(startTime), + StartTime: startTime, + EndTime: endTime, + Duration: endTime.Sub(startTime), } } diff --git a/serve.go b/serve.go index 3926b7161..a45d1f56a 100644 --- a/serve.go +++ b/serve.go @@ -10,7 +10,6 @@ import ( "time" "github.com/aelsabbahy/goss/outputs" - "github.com/aelsabbahy/goss/resource" "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" "github.com/fatih/color" @@ -112,13 +111,12 @@ func (h healthHandler) processAndEnsureCached(negotiatedContentType string, outp func (h healthHandler) runValidate(outputer outputs.Outputer) res { h.sys = system.New(h.c.PackageManager) - iStartTime := time.Now() out := validate(h.sys, h.gossConfig, h.maxConcurrent) var b bytes.Buffer outputConfig := util.OutputConfig{ FormatOptions: h.c.FormatOptions, } - exitCode := outputer.Output(&b, out, iStartTime, outputConfig) + exitCode := outputer.Output(&b, out, outputConfig) resp := res{ body: b, } @@ -167,12 +165,3 @@ func (h healthHandler) responseContentType(outputName string) string { } return fmt.Sprintf("%s%s", mediaTypePrefix, outputName) } - -func (h healthHandler) renderBody(results <-chan []resource.TestResult, outputer outputs.Outputer) (int, bytes.Buffer) { - outputConfig := util.OutputConfig{ - FormatOptions: h.c.FormatOptions, - } - var b bytes.Buffer - exitCode := outputer.Output(&b, results, time.Now(), outputConfig) - return exitCode, b -} diff --git a/validate.go b/validate.go index f106d44aa..e9a05f106 100644 --- a/validate.go +++ b/validate.go @@ -100,7 +100,7 @@ func ValidateResults(c *util.Config) (results <-chan []resource.TestResult, err // and supports retries and more, this is the full featured Validate used // by the typical CLI invocation and will produce output to StdOut. Use // ValidateResults for programmatic access -func Validate(c *util.Config, startTime time.Time) (code int, err error) { +func Validate(c *util.Config) (code int, err error) { // Needed for contains-elements // Maybe we don't use this and use custom // contain_element_matcher is needed because it's single entry to avoid @@ -130,10 +130,10 @@ func Validate(c *util.Config, startTime time.Time) (code int, err error) { sleep := c.Sleep retryTimeout := c.RetryTimeout i := 1 + startTime := time.Now() for { - iStartTime := time.Now() out := validate(sys, *gossConfig, c.MaxConcurrent) - exitCode := outputer.Output(ofh, out, iStartTime, outputConfig) + exitCode := outputer.Output(ofh, out, outputConfig) if retryTimeout == 0 || exitCode == 0 { return exitCode, nil } From 04eea68471e720f955f4a5f5e4be73cc7442c54a Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 18 Feb 2023 11:30:06 -0800 Subject: [PATCH 21/41] Cache test results in serve instead of output closes #612 --- serve.go | 53 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/serve.go b/serve.go index a45d1f56a..9c4c73e02 100644 --- a/serve.go +++ b/serve.go @@ -10,6 +10,7 @@ import ( "time" "github.com/aelsabbahy/goss/outputs" + "github.com/aelsabbahy/goss/resource" "github.com/aelsabbahy/goss/system" "github.com/aelsabbahy/goss/util" "github.com/fatih/color" @@ -89,34 +90,28 @@ func (h healthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } func (h healthHandler) processAndEnsureCached(negotiatedContentType string, outputer outputs.Outputer) res { - cacheKey := fmt.Sprintf("res:%s", negotiatedContentType) + var tra [][]resource.TestResult + cacheKey := "res" tmp, found := h.cache.Get(cacheKey) - if found { - return tmp.(res) - } - - h.gossMu.Lock() - defer h.gossMu.Unlock() - tmp, found = h.cache.Get(cacheKey) if found { log.Printf("Returning cached[%s].", cacheKey) - return tmp.(res) + tra = tmp.([][]resource.TestResult) + } else { + log.Printf("Stale cache[%s], running tests", cacheKey) + h.sys = system.New(h.c.PackageManager) + tra = h.validate() + h.cache.SetDefault(cacheKey, tra) } - - log.Printf("Stale cache[%s], running tests", cacheKey) - resp := h.runValidate(outputer) - h.cache.SetDefault(cacheKey, resp) - return resp + trc := testResultArrayToChan(tra) + return h.output(trc, outputer) } -func (h healthHandler) runValidate(outputer outputs.Outputer) res { - h.sys = system.New(h.c.PackageManager) - out := validate(h.sys, h.gossConfig, h.maxConcurrent) +func (h healthHandler) output(trc <-chan []resource.TestResult, outputer outputs.Outputer) res { var b bytes.Buffer outputConfig := util.OutputConfig{ FormatOptions: h.c.FormatOptions, } - exitCode := outputer.Output(&b, out, outputConfig) + exitCode := outputer.Output(&b, trc, outputConfig) resp := res{ body: b, } @@ -127,6 +122,28 @@ func (h healthHandler) runValidate(outputer outputs.Outputer) res { } return resp } +func (h healthHandler) validate() [][]resource.TestResult { + h.sys = system.New(h.c.PackageManager) + res := make([][]resource.TestResult, 0) + tr := validate(h.sys, h.gossConfig, h.maxConcurrent) + for i := range tr { + res = append(res, i) + } + return res +} + +func testResultArrayToChan(tra [][]resource.TestResult) <-chan []resource.TestResult { + c := make(chan []resource.TestResult) + go func(c chan []resource.TestResult) { + defer close(c) + + for _, i := range tra { + c <- i + } + }(c) + + return c +} const ( // https://en.wikipedia.org/wiki/Media_type From 2a8c9e5ef0194693b594e4783a29e63fd7adae2e Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 18 Feb 2023 12:02:18 -0800 Subject: [PATCH 22/41] Use exit code 78 if test file is unparseable closes #317 --- validate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validate.go b/validate.go index e9a05f106..5a9fab375 100644 --- a/validate.go +++ b/validate.go @@ -112,7 +112,7 @@ func Validate(c *util.Config) (code int, err error) { gossConfig, err := getGossConfig(c.Vars, c.VarsInline, c.Spec) if err != nil { - return 1, err + return 78, err } sys := system.New(c.PackageManager) From ef62d85450e139bdb915ffe75e1a3bae3e87a449 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sun, 19 Feb 2023 08:39:25 -0800 Subject: [PATCH 23/41] Full EVR for rpm and fix failing tests --- examples/goss_awesome_gomega.yaml | 8 +++++--- goss_test.go | 3 +-- integration-tests/goss/centos7/goss-aa-expected.yaml | 2 +- integration-tests/goss/centos7/goss-expected.yaml | 2 +- integration-tests/goss/vars.yaml | 2 +- resource/validate.go | 4 ++-- serve_test.go | 4 ++-- system/package_rpm.go | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/examples/goss_awesome_gomega.yaml b/examples/goss_awesome_gomega.yaml index 46a693e11..029eef03e 100644 --- a/examples/goss_awesome_gomega.yaml +++ b/examples/goss_awesome_gomega.yaml @@ -69,11 +69,13 @@ matching: test_array: content: - - '46' - '45' - matches: - - and: [{ge: 40}, {le: 50}] - '46' + - '47' + matches: + - contain-element: {match-regexp: "4."} + - '45' + - and: [{ge: 46}, {le: 50}] test_reader_using_string_matchers: content: | diff --git a/goss_test.go b/goss_test.go index 5af7cffdf..37fb98373 100644 --- a/goss_test.go +++ b/goss_test.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "os" "testing" - "time" "github.com/aelsabbahy/goss/outputs" "github.com/aelsabbahy/goss/resource" @@ -92,7 +91,7 @@ func TestUseAsPackage(t *testing.T) { } } - code, err := Validate(cfg, time.Now()) + code, err := Validate(cfg) checkErr(t, err, "check failed") if code != 0 { t.Fatalf("check failed, expected 0 got %d", code) diff --git a/integration-tests/goss/centos7/goss-aa-expected.yaml b/integration-tests/goss/centos7/goss-aa-expected.yaml index 13ba204dc..a7aa4ce4d 100644 --- a/integration-tests/goss/centos7/goss-aa-expected.yaml +++ b/integration-tests/goss/centos7/goss-aa-expected.yaml @@ -2,7 +2,7 @@ package: httpd: installed: true versions: - - 2.4.6 + - 2.4.6-95.el7.centos port: tcp:80: listening: true diff --git a/integration-tests/goss/centos7/goss-expected.yaml b/integration-tests/goss/centos7/goss-expected.yaml index 5bbd0108b..a8404712c 100644 --- a/integration-tests/goss/centos7/goss-expected.yaml +++ b/integration-tests/goss/centos7/goss-expected.yaml @@ -15,7 +15,7 @@ package: httpd: installed: true versions: - - 2.4.6 + - 2.4.6-95.el7.centos vim-tiny: installed: false addr: diff --git a/integration-tests/goss/vars.yaml b/integration-tests/goss/vars.yaml index 66a205c68..cbf6df778 100644 --- a/integration-tests/goss/vars.yaml +++ b/integration-tests/goss/vars.yaml @@ -10,7 +10,7 @@ arch: centos7: proxy: http://127.0.0.1:8888 packages: - httpd: "2.4.6" + httpd: "2.4.6-95.el7.centos" services: httpd: [] trusty: diff --git a/resource/validate.go b/resource/validate.go index 30ee5c621..598b836e8 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -40,8 +40,8 @@ type TestResult struct { Result string `json:"result" yaml:"result"` Err error `json:"err" yaml:"err"` MatcherResult matchers.MatcherResult `json:"matcher-result" yaml:"matcher-result"` - StartTime time.Time `json:"duration" yaml:"duration"` - EndTime time.Time `json:"duration" yaml:"duration"` + StartTime time.Time `json:"start-time" yaml:"start-time"` + EndTime time.Time `json:"end-time" yaml:"end-time"` Duration time.Duration `json:"duration" yaml:"duration"` } diff --git a/serve_test.go b/serve_test.go index bf55458b9..84496001d 100644 --- a/serve_test.go +++ b/serve_test.go @@ -268,14 +268,14 @@ func TestServeCacheNegotiatingContent(t *testing.T) { logOutput.Reset() }) - t.Run("immediately re-request but different accept header, cache should be cold", func(t *testing.T) { + t.Run("immediately re-request but different accept header, cache should be warm", func(t *testing.T) { req := makeRequest(t, config, map[string][]string{ "accept": {"application/vnd.goss-rspecish"}, }) handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Result().StatusCode) - assert.Contains(t, logOutput.String(), "Stale cache") + assert.NotContains(t, logOutput.String(), "Stale cache") t.Log(logOutput.String()) logOutput.Reset() }) diff --git a/system/package_rpm.go b/system/package_rpm.go index 4537c3395..e0bb19afd 100644 --- a/system/package_rpm.go +++ b/system/package_rpm.go @@ -23,7 +23,7 @@ func (p *RpmPackage) setup() { return } p.loaded = true - cmd := util.NewCommand("rpm", "-q", "--nosignature", "--nohdrchk", "--nodigest", "--qf", "%{VERSION}\n", p.name) + cmd := util.NewCommand("rpm", "-q", "--nosignature", "--nohdrchk", "--nodigest", "--qf", "%|EPOCH?{%{EPOCH}:}:{}|%{VERSION}-%{RELEASE}\n", p.name) if err := cmd.Run(); err != nil { return } From aa990cffd7613ea1ada77ac5450af5c5ab9c88f3 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Tue, 18 Apr 2023 10:36:54 -0700 Subject: [PATCH 24/41] more tests --- Makefile | 13 + examples/goss_awesome_gomega.yaml | 2 +- go.mod | 3 + go.sum | 11 +- outputs/outputs_test.go | 2 +- outputs/rspecish.go | 3 + resource/matching.go | 4 + testdata/matching_basic.0.documentation | 21 ++ testdata/matching_basic.0.nagios | 1 + testdata/matching_basic.0.rspecish | 8 + testdata/matching_basic.0.tap | 14 ++ testdata/matching_basic.1.documentation | 27 +++ testdata/matching_basic.1.rspecish | 12 + testdata/matching_basic.1.tap | 14 ++ testdata/matching_basic.2.nagios | 1 + testdata/matching_basic.78.documentation | 0 testdata/matching_basic.78.nagios | 0 testdata/matching_basic.78.rspecish | 0 testdata/matching_basic.78.tap | 0 testdata/matching_basic.yaml | 107 +++++++++ .../matching_basic_failing.1.documentation | 178 ++++++++++++++ testdata/matching_basic_failing.1.rspecish | 99 ++++++++ testdata/matching_basic_failing.1.tap | 15 ++ testdata/matching_basic_failing.2.nagios | 1 + testdata/matching_basic_failing.yaml | 114 +++++++++ .../matching_basic_string.0.documentation | 8 + testdata/matching_basic_string.0.nagios | 1 + testdata/matching_basic_string.0.rspecish | 4 + testdata/matching_basic_string.0.tap | 5 + .../matching_transformers.0.documentation | 15 ++ testdata/matching_transformers.0.nagios | 1 + testdata/matching_transformers.0.rspecish | 4 + testdata/matching_transformers.0.tap | 12 + testdata/matching_transformers.yaml | 119 ++++++++++ ...ching_transformers_failing.1.documentation | 223 ++++++++++++++++++ .../matching_transformers_failing.1.rspecish | 122 ++++++++++ testdata/matching_transformers_failing.1.tap | 16 ++ .../matching_transformers_failing.2.nagios | 1 + testdata/matching_transformers_failing.yaml | 141 +++++++++++ validate.go | 13 +- 40 files changed, 1326 insertions(+), 9 deletions(-) create mode 100644 testdata/matching_basic.0.documentation create mode 100644 testdata/matching_basic.0.nagios create mode 100644 testdata/matching_basic.0.rspecish create mode 100644 testdata/matching_basic.0.tap create mode 100644 testdata/matching_basic.1.documentation create mode 100644 testdata/matching_basic.1.rspecish create mode 100644 testdata/matching_basic.1.tap create mode 100644 testdata/matching_basic.2.nagios create mode 100644 testdata/matching_basic.78.documentation create mode 100644 testdata/matching_basic.78.nagios create mode 100644 testdata/matching_basic.78.rspecish create mode 100644 testdata/matching_basic.78.tap create mode 100644 testdata/matching_basic.yaml create mode 100644 testdata/matching_basic_failing.1.documentation create mode 100644 testdata/matching_basic_failing.1.rspecish create mode 100644 testdata/matching_basic_failing.1.tap create mode 100644 testdata/matching_basic_failing.2.nagios create mode 100644 testdata/matching_basic_failing.yaml create mode 100644 testdata/matching_basic_string.0.documentation create mode 100644 testdata/matching_basic_string.0.nagios create mode 100644 testdata/matching_basic_string.0.rspecish create mode 100644 testdata/matching_basic_string.0.tap create mode 100644 testdata/matching_transformers.0.documentation create mode 100644 testdata/matching_transformers.0.nagios create mode 100644 testdata/matching_transformers.0.rspecish create mode 100644 testdata/matching_transformers.0.tap create mode 100644 testdata/matching_transformers.yaml create mode 100644 testdata/matching_transformers_failing.1.documentation create mode 100644 testdata/matching_transformers_failing.1.rspecish create mode 100644 testdata/matching_transformers_failing.1.tap create mode 100644 testdata/matching_transformers_failing.2.nagios create mode 100644 testdata/matching_transformers_failing.yaml diff --git a/Makefile b/Makefile index a5e1aa8c1..a6b349fbc 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,19 @@ test: $(info INFO: Starting build $@) ./ci/go-test.sh $(pkgs) +cov: + go test -coverpkg=./... -coverprofile=c.out ./... + # go tool cover -func ./c.out + +funcov: + go test -coverpkg=./... -coverprofile=c.out ./... + go tool cover -func ./c.out + +htmlcov: + go test -v -coverpkg=./... -coverprofile=c.out ./... + go tool cover -html ./c.out + + lint: $(info INFO: Starting build $@) golint $(pkgs) || true diff --git a/examples/goss_awesome_gomega.yaml b/examples/goss_awesome_gomega.yaml index 029eef03e..1a9a54aa8 100644 --- a/examples/goss_awesome_gomega.yaml +++ b/examples/goss_awesome_gomega.yaml @@ -28,6 +28,7 @@ matching: - '!wtf' - '!/^ERROR:/' + # Transformers basic_reader_as_array: as-reader: true content: | @@ -40,7 +41,6 @@ matching: - not: {contain-substring: 'wtf'} - not: {match-regexp: '^ERROR:'} - # Transformers test_numeric_string: content: 128 matches: diff --git a/go.mod b/go.mod index fbcab0fd5..fe1097a60 100644 --- a/go.mod +++ b/go.mod @@ -9,12 +9,14 @@ require ( github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 github.com/blang/semver v3.5.1+incompatible github.com/cheekybits/genny v1.0.0 + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.10.0 github.com/go-ole/go-ole v1.2.4 // indirect github.com/google/uuid v1.1.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/icza/dyno v0.0.0-20200205103839-49cb13720835 github.com/imdario/mergo v0.3.11 // indirect + github.com/kr/pretty v0.1.0 // indirect github.com/miekg/dns v1.1.35 github.com/mitchellh/copystructure v1.0.0 // indirect github.com/moby/sys/mountinfo v0.4.0 @@ -30,6 +32,7 @@ require ( github.com/urfave/cli v1.22.5 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.0.3 ) diff --git a/go.sum b/go.sum index 1f61d67d6..56bb87f21 100644 --- a/go.sum +++ b/go.sum @@ -17,8 +17,9 @@ github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitf github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= @@ -46,6 +47,11 @@ github.com/icza/dyno v0.0.0-20200205103839-49cb13720835 h1:f1irK5f03uGGj+FjgQfZ5 github.com/icza/dyno v0.0.0-20200205103839-49cb13720835/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= @@ -144,8 +150,9 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/outputs/outputs_test.go b/outputs/outputs_test.go index 284a63d10..2dea77e69 100644 --- a/outputs/outputs_test.go +++ b/outputs/outputs_test.go @@ -41,5 +41,5 @@ func TestOutputFormatOptions(t *testing.T) { assert.Contains(t, list, foPerfData) assert.Contains(t, list, foPretty) assert.Contains(t, list, foVerbose) - assert.Len(t, list, 3) + assert.Len(t, list, 4) } diff --git a/outputs/rspecish.go b/outputs/rspecish.go index f527716c5..6d00e6e48 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -18,6 +18,9 @@ func (r Rspecish) ValidOptions() []*formatOption { func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { + sort := util.IsValueInList(foSort, outConfig.FormatOptions) + results = getResults(results, sort) + var startTime time.Time var endTime time.Time testCount := 0 diff --git a/resource/matching.go b/resource/matching.go index 05346bb97..b27df32b6 100644 --- a/resource/matching.go +++ b/resource/matching.go @@ -18,6 +18,7 @@ type Matching struct { AsReader bool `json:"as-reader,omitempty" yaml:"as-reader,omitempty"` id string `json:"-" yaml:"-"` Matches matcher `json:"matches" yaml:"matches"` + Skip bool `json:"skip,omitempty" yaml:"skip,omitempty"` } type MatchingMap map[string]*Matching @@ -31,6 +32,9 @@ func (r *Matching) GetMeta() meta { return r.Meta } func (a *Matching) Validate(sys *system.System) []TestResult { skip := false + if a.Skip { + skip = true + } var stub interface{} if a.AsReader { diff --git a/testdata/matching_basic.0.documentation b/testdata/matching_basic.0.documentation new file mode 100644 index 000000000..d7cd23535 --- /dev/null +++ b/testdata/matching_basic.0.documentation @@ -0,0 +1,21 @@ +Matching: basic_array: matches: matches expectation: ["group1","group2"] +Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} +Matching: basic_int: matches: matches expectation: 42 +Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +Matching: basic_string: matches: matches expectation: "this is a test" +Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} +Matching: basic_string_skip: matches: skipped +Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} +Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} +Matching: negated_basic_int: matches: matches expectation: {"not":43} +Matching: negated_basic_reader: matches: matches expectation: {"not":{"contain-elements":["fox","/^t.*w$/","!foo","!/^foo/"]}} +Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} +Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} + + +Failures/Skipped: + +Matching: basic_string_skip: matches: skipped + +Total Duration: +Count: 13, Failed: 0, Skipped: 1 diff --git a/testdata/matching_basic.0.nagios b/testdata/matching_basic.0.nagios new file mode 100644 index 000000000..b3d7a9e78 --- /dev/null +++ b/testdata/matching_basic.0.nagios @@ -0,0 +1 @@ +GOSS OK - Count: 13, Failed: 0, Skipped: 1, Duration: diff --git a/testdata/matching_basic.0.rspecish b/testdata/matching_basic.0.rspecish new file mode 100644 index 000000000..5ef0526d5 --- /dev/null +++ b/testdata/matching_basic.0.rspecish @@ -0,0 +1,8 @@ +......S...... + +Failures/Skipped: + +Matching: basic_string_skip: matches: skipped + +Total Duration: +Count: 13, Failed: 0, Skipped: 1 diff --git a/testdata/matching_basic.0.tap b/testdata/matching_basic.0.tap new file mode 100644 index 000000000..583a03b12 --- /dev/null +++ b/testdata/matching_basic.0.tap @@ -0,0 +1,14 @@ +1..13 +ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] +ok 2 - Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} +ok 3 - Matching: basic_int: matches: matches expectation: 42 +ok 4 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +ok 5 - Matching: basic_string: matches: matches expectation: "this is a test" +ok 6 - Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} +ok 7 - # SKIP Matching: basic_string_skip: matches: skipped +ok 8 - Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} +ok 9 - Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} +ok 10 - Matching: negated_basic_int: matches: matches expectation: {"not":43} +ok 11 - Matching: negated_basic_reader: matches: matches expectation: {"not":{"contain-elements":["fox","/^t.*w$/","!foo","!/^foo/"]}} +ok 12 - Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} +ok 13 - Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} diff --git a/testdata/matching_basic.1.documentation b/testdata/matching_basic.1.documentation new file mode 100644 index 000000000..03d5dcbc1 --- /dev/null +++ b/testdata/matching_basic.1.documentation @@ -0,0 +1,27 @@ +Matching: basic_array: matches: matches expectation: ["group1","group2"] +Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} +Matching: basic_int: matches: matches expectation: 42 +Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +Matching: basic_string: matches: matches expectation: "this is a test" +Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} +Matching: basic_string_skip: matches: skipped +Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} +Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} +Matching: negated_basic_int: matches: matches expectation: {"not":43} +Matching: negated_basic_reader: matches: +Error + Unknown matcher: contains-elements +Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} +Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} + + +Failures/Skipped: + +Matching: basic_string_skip: matches: skipped + +Matching: negated_basic_reader: matches: +Error + Unknown matcher: contains-elements + +Total Duration: +Count: 13, Failed: 1, Skipped: 1 diff --git a/testdata/matching_basic.1.rspecish b/testdata/matching_basic.1.rspecish new file mode 100644 index 000000000..523a589b1 --- /dev/null +++ b/testdata/matching_basic.1.rspecish @@ -0,0 +1,12 @@ +SF........... + +Failures/Skipped: + +Matching: basic_string_skip: matches: skipped + +Matching: negated_basic_reader: matches: +Error + Unknown matcher: contains-elements + +Total Duration: +Count: 13, Failed: 1, Skipped: 1 diff --git a/testdata/matching_basic.1.tap b/testdata/matching_basic.1.tap new file mode 100644 index 000000000..7fa444668 --- /dev/null +++ b/testdata/matching_basic.1.tap @@ -0,0 +1,14 @@ +1..13 +ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] +ok 2 - Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} +ok 3 - Matching: basic_int: matches: matches expectation: 42 +ok 4 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +ok 5 - Matching: basic_string: matches: matches expectation: "this is a test" +ok 6 - Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} +ok 7 - # SKIP Matching: basic_string_skip: matches: skipped +ok 8 - Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} +ok 9 - Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} +ok 10 - Matching: negated_basic_int: matches: matches expectation: {"not":43} +not ok 11 - Matching: negated_basic_reader: matches: Error Unknown matcher: contains-elements +ok 12 - Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} +ok 13 - Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} diff --git a/testdata/matching_basic.2.nagios b/testdata/matching_basic.2.nagios new file mode 100644 index 000000000..3d0c16687 --- /dev/null +++ b/testdata/matching_basic.2.nagios @@ -0,0 +1 @@ +GOSS CRITICAL - Count: 13, Failed: 1, Skipped: 1, Duration: diff --git a/testdata/matching_basic.78.documentation b/testdata/matching_basic.78.documentation new file mode 100644 index 000000000..e69de29bb diff --git a/testdata/matching_basic.78.nagios b/testdata/matching_basic.78.nagios new file mode 100644 index 000000000..e69de29bb diff --git a/testdata/matching_basic.78.rspecish b/testdata/matching_basic.78.rspecish new file mode 100644 index 000000000..e69de29bb diff --git a/testdata/matching_basic.78.tap b/testdata/matching_basic.78.tap new file mode 100644 index 000000000..e69de29bb diff --git a/testdata/matching_basic.yaml b/testdata/matching_basic.yaml new file mode 100644 index 000000000..323ad3f0a --- /dev/null +++ b/testdata/matching_basic.yaml @@ -0,0 +1,107 @@ +matching: + # Basic matchers + basic_string: + content: 'this is a test' + matches: 'this is a test' + + basic_string_regexp: + content: 'this is a test' + matches: + match-regexp: '^this' + + basic_string_skip: + skip: true + content: 'this is a test' + matches: 'this is a test' + + basic_int: + content: 42 + matches: 42 + + basic_array: + content: + - 'group1' + - 'group2' + - 'group3' + matches: + - 'group1' + - 'group2' + + basic_array_matchers: + content: [foo, bar, moo] + matches: + and: + - contain-elements: [foo, bar] + - [foo, bar] # same as above + - equal: [foo, bar, moo] # order matters, exact match + - consist-of: [foo, have-prefix: m, bar] # order doesn't matter, can use matchers + - contain-element: + have-prefix: b + + basic_reader: + as-reader: true + content: | + foo bar + moo cow + matches: + - 'foo' + - '/^m.*w$/' + - '!wtf' + - '!/^ERROR:/' + + # Negated + negated_basic_string: + content: 'this is a test' + matches: + not: 'this is a failing test' + + negated_basic_string_regexp: + content: 'this is a test' + matches: + not: + match-regexp: '^foo' + + negated_basic_int: + content: 42 + matches: + not: 43 + + negated_basic_array: + content: + - 'group1' + - 'group2' + - 'group3' + matches: + not: + - 'group1' + - 'group2' + - 'group2' + - 'group4' + + negated_basic_array_matchers: + content: [foo, bar, moo] + matches: + and: + - not: + contain-elements: [fox, box] + - not: [fox, bax] # same as above + - not: + equal: [fox, bax, mox] # order matters, exact match + - not: + consist-of: [fox, have-prefix: t, box] # order doesn't matter, can use matchers + - not: + contain-element: + have-prefix: x + + negated_basic_reader: + as-reader: true + content: | + foo bar + moo cow + matches: + not: + contain-elements: + - 'fox' + - '/^t.*w$/' + - '!foo' + - '!/^foo/' diff --git a/testdata/matching_basic_failing.1.documentation b/testdata/matching_basic_failing.1.documentation new file mode 100644 index 000000000..8136fe5fa --- /dev/null +++ b/testdata/matching_basic_failing.1.documentation @@ -0,0 +1,178 @@ +Matching: basic_array: matches: +Expected + ["group1","group2","group3"] +to contain elements + ["group1","group2","group2","group4"] +the missing elements were + ["group2","group4"] +Matching: basic_array_consists_of: matches: +Expected + ["foo","bar","moo"] +to consist of + ["fox",{"have-prefix":"t"},"box"] +the missing elements were + ["fox",{"have-prefix":"t"},"box"] +the extra elements were + ["foo","bar","moo"] +Matching: basic_array_matchers: matches: +Expected + ["foo","bar","moo"] +To satisfy at least one of these matchers + [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] +Matching: basic_int: matches: +Expected + 42 +to be == + 43 +Matching: basic_reader: matches: +Expected + "object: *strings.Reader" +to contain patterns + ["fox","/^t.*w$/","!foo","!/^foo/"] +the missing elements were + ["fox","/^t.*w$/","!foo","!/^foo/"] +Matching: basic_string: matches: +Expected + "this is a test" +to equal + "this is a failing test" +Matching: basic_string_regexp: matches: +Expected + "this is a test" +to match regular expression + "^foo" +Matching: negated_basic_array: matches: +Expected + ["group1","group2","group3"] +not to contain elements + ["group1","group2","group3"] +Matching: negated_basic_array_consists_of: matches: +Expected + ["foo","bar","moo"] +not to consist of + ["foo",{"have-prefix":"m"},"bar"] +Matching: negated_basic_array_matchers: matches: +Expected + ["foo","bar","moo"] +To satisfy at least one of these matchers + [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] +Matching: negated_basic_int: matches: +Expected + 42 +not to be == + 42 +Matching: negated_basic_reader: matches: +Error + ContainElements matcher expects an array/slice/map. Got: + : foo bar + moo cow + +Matching: negated_basic_string: matches: +Expected + "this is a test" +not to equal + "this is a test" +Matching: negatedbasic_string_regexp: matches: +Expected + "this is a test" +not to match regular expression + "^this" + + +Failures/Skipped: + +Matching: basic_array: matches: +Expected + ["group1","group2","group3"] +to contain elements + ["group1","group2","group2","group4"] +the missing elements were + ["group2","group4"] + +Matching: basic_array_consists_of: matches: +Expected + ["foo","bar","moo"] +to consist of + ["fox",{"have-prefix":"t"},"box"] +the missing elements were + ["fox",{"have-prefix":"t"},"box"] +the extra elements were + ["foo","bar","moo"] + +Matching: basic_array_matchers: matches: +Expected + ["foo","bar","moo"] +To satisfy at least one of these matchers + [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] + +Matching: basic_int: matches: +Expected + 42 +to be == + 43 + +Matching: basic_reader: matches: +Expected + "object: *strings.Reader" +to contain patterns + ["fox","/^t.*w$/","!foo","!/^foo/"] +the missing elements were + ["fox","/^t.*w$/","!foo","!/^foo/"] + +Matching: basic_string: matches: +Expected + "this is a test" +to equal + "this is a failing test" + +Matching: basic_string_regexp: matches: +Expected + "this is a test" +to match regular expression + "^foo" + +Matching: negated_basic_array: matches: +Expected + ["group1","group2","group3"] +not to contain elements + ["group1","group2","group3"] + +Matching: negated_basic_array_consists_of: matches: +Expected + ["foo","bar","moo"] +not to consist of + ["foo",{"have-prefix":"m"},"bar"] + +Matching: negated_basic_array_matchers: matches: +Expected + ["foo","bar","moo"] +To satisfy at least one of these matchers + [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] + +Matching: negated_basic_int: matches: +Expected + 42 +not to be == + 42 + +Matching: negated_basic_reader: matches: +Error + ContainElements matcher expects an array/slice/map. Got: + : foo bar + moo cow + + +Matching: negated_basic_string: matches: +Expected + "this is a test" +not to equal + "this is a test" + +Matching: negatedbasic_string_regexp: matches: +Expected + "this is a test" +not to match regular expression + "^this" + +Total Duration: +Count: 14, Failed: 14, Skipped: 0 diff --git a/testdata/matching_basic_failing.1.rspecish b/testdata/matching_basic_failing.1.rspecish new file mode 100644 index 000000000..09d4e4ba7 --- /dev/null +++ b/testdata/matching_basic_failing.1.rspecish @@ -0,0 +1,99 @@ +FFFFFFFFFFFFFF + +Failures/Skipped: + +Matching: basic_array: matches: +Expected + ["group1","group2","group3"] +to contain elements + ["group1","group2","group2","group4"] +the missing elements were + ["group2","group4"] + +Matching: basic_array_consists_of: matches: +Expected + ["foo","bar","moo"] +to consist of + ["fox",{"have-prefix":"t"},"box"] +the missing elements were + ["fox",{"have-prefix":"t"},"box"] +the extra elements were + ["foo","bar","moo"] + +Matching: basic_array_matchers: matches: +Expected + ["foo","bar","moo"] +To satisfy at least one of these matchers + [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] + +Matching: basic_int: matches: +Expected + 42 +to be == + 43 + +Matching: basic_reader: matches: +Expected + "object: *strings.Reader" +to contain patterns + ["fox","/^t.*w$/","!foo","!/^foo/"] +the missing elements were + ["fox","/^t.*w$/","!foo","!/^foo/"] + +Matching: basic_string: matches: +Expected + "this is a test" +to equal + "this is a failing test" + +Matching: basic_string_regexp: matches: +Expected + "this is a test" +to match regular expression + "^foo" + +Matching: negated_basic_array: matches: +Expected + ["group1","group2","group3"] +not to contain elements + ["group1","group2","group3"] + +Matching: negated_basic_array_consists_of: matches: +Expected + ["foo","bar","moo"] +not to consist of + ["foo",{"have-prefix":"m"},"bar"] + +Matching: negated_basic_array_matchers: matches: +Expected + ["foo","bar","moo"] +To satisfy at least one of these matchers + [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] + +Matching: negated_basic_int: matches: +Expected + 42 +not to be == + 42 + +Matching: negated_basic_reader: matches: +Error + ContainElements matcher expects an array/slice/map. Got: + : foo bar + moo cow + + +Matching: negated_basic_string: matches: +Expected + "this is a test" +not to equal + "this is a test" + +Matching: negatedbasic_string_regexp: matches: +Expected + "this is a test" +not to match regular expression + "^this" + +Total Duration: +Count: 14, Failed: 14, Skipped: 0 diff --git a/testdata/matching_basic_failing.1.tap b/testdata/matching_basic_failing.1.tap new file mode 100644 index 000000000..4afe4cd3e --- /dev/null +++ b/testdata/matching_basic_failing.1.tap @@ -0,0 +1,15 @@ +1..14 +not ok 1 - Matching: basic_array: matches: Expected ["group1","group2","group3"] to contain elements ["group1","group2","group2","group4"] the missing elements were ["group2","group4"] +not ok 2 - Matching: basic_array_consists_of: matches: Expected ["foo","bar","moo"] to consist of ["fox",{"have-prefix":"t"},"box"] the missing elements were ["fox",{"have-prefix":"t"},"box"] the extra elements were ["foo","bar","moo"] +not ok 3 - Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] +not ok 4 - Matching: basic_int: matches: Expected 42 to be == 43 +not ok 5 - Matching: basic_reader: matches: Expected "object: *strings.Reader" to contain patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] +not ok 6 - Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test" +not ok 7 - Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" +not ok 8 - Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] not to contain elements ["group1","group2","group3"] +not ok 9 - Matching: negated_basic_array_consists_of: matches: Expected ["foo","bar","moo"] not to consist of ["foo",{"have-prefix":"m"},"bar"] +not ok 10 - Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] +not ok 11 - Matching: negated_basic_int: matches: Expected 42 not to be == 42 +not ok 12 - Matching: negated_basic_reader: matches: Error ContainElements matcher expects an array/slice/map. Got: : foo bar moo cow +not ok 13 - Matching: negated_basic_string: matches: Expected "this is a test" not to equal "this is a test" +not ok 14 - Matching: negatedbasic_string_regexp: matches: Expected "this is a test" not to match regular expression "^this" diff --git a/testdata/matching_basic_failing.2.nagios b/testdata/matching_basic_failing.2.nagios new file mode 100644 index 000000000..5cebddf7b --- /dev/null +++ b/testdata/matching_basic_failing.2.nagios @@ -0,0 +1 @@ +GOSS CRITICAL - Count: 14, Failed: 14, Skipped: 0, Duration: diff --git a/testdata/matching_basic_failing.yaml b/testdata/matching_basic_failing.yaml new file mode 100644 index 000000000..67acc87e2 --- /dev/null +++ b/testdata/matching_basic_failing.yaml @@ -0,0 +1,114 @@ +matching: + # Basic matchers + basic_string: + content: 'this is a test' + matches: 'this is a failing test' + + basic_string_regexp: + content: 'this is a test' + matches: + match-regexp: '^foo' + + basic_int: + content: 42 + matches: 43 + + basic_array: + content: + - 'group1' + - 'group2' + - 'group3' + matches: + - 'group1' + - 'group2' + - 'group2' + - 'group4' + + basic_array_matchers: + content: [foo, bar, moo] + matches: + or: + - contain-elements: [fox, box] + - [fox, bax] # same as above + - equal: [fox, bax, mox] # order matters, exact match + - consist-of: [fox, have-prefix: t, box] # order doesn't matter, can use matchers + - contain-element: + have-prefix: x + + basic_array_consists_of: + content: [foo, bar, moo] + matches: + consist-of: [fox, have-prefix: t, box] # order doesn't matter, can use matchers + + basic_reader: + as-reader: true + content: | + foo bar + moo cow + matches: + - 'fox' + - '/^t.*w$/' + - '!foo' + - '!/^foo/' + + # Negated + negated_basic_string: + content: 'this is a test' + matches: + not: 'this is a test' + + negatedbasic_string_regexp: + content: 'this is a test' + matches: + not: + match-regexp: '^this' + + negated_basic_int: + content: 42 + matches: + not: 42 + + negated_basic_array: + content: + - 'group1' + - 'group2' + - 'group3' + matches: + not: + - 'group1' + - 'group2' + - 'group3' + + negated_basic_array_matchers: + content: [foo, bar, moo] + matches: + or: + - not: + contain-elements: [foo, bar] + - not: + [foo, bar] # same as above + - not: + equal: [foo, bar, moo] # order matters, exact match + - not: + consist-of: [foo, have-prefix: m, bar] # order doesn't matter, can use matchers + - not: + contain-element: + have-prefix: b + + negated_basic_array_consists_of: + content: [foo, bar, moo] + matches: + not: + consist-of: [foo, have-prefix: m, bar] # order doesn't matter, can use matchers + + negated_basic_reader: + as-reader: true + content: | + foo bar + moo cow + matches: + not: + - 'foo' + - '/^m.*w$/' + - '!wtf' + - '!/^ERROR:/' diff --git a/testdata/matching_basic_string.0.documentation b/testdata/matching_basic_string.0.documentation new file mode 100644 index 000000000..028bdfb3a --- /dev/null +++ b/testdata/matching_basic_string.0.documentation @@ -0,0 +1,8 @@ +Matching: basic_array: matches: matches expectation: ["group1","group2"] +Matching: basic_int: matches: matches expectation: 42 +Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +Matching: basic_string: matches: matches expectation: "this is a test" + + +Total Duration: 0.000s +Count: 4, Failed: 0, Skipped: 0 diff --git a/testdata/matching_basic_string.0.nagios b/testdata/matching_basic_string.0.nagios new file mode 100644 index 000000000..18fc27535 --- /dev/null +++ b/testdata/matching_basic_string.0.nagios @@ -0,0 +1 @@ +GOSS OK - Count: 4, Failed: 0, Skipped: 0, Duration: 0.000s diff --git a/testdata/matching_basic_string.0.rspecish b/testdata/matching_basic_string.0.rspecish new file mode 100644 index 000000000..c76e69d36 --- /dev/null +++ b/testdata/matching_basic_string.0.rspecish @@ -0,0 +1,4 @@ +.... + +Total Duration: 0.000s +Count: 4, Failed: 0, Skipped: 0 diff --git a/testdata/matching_basic_string.0.tap b/testdata/matching_basic_string.0.tap new file mode 100644 index 000000000..29923fbc5 --- /dev/null +++ b/testdata/matching_basic_string.0.tap @@ -0,0 +1,5 @@ +1..4 +ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] +ok 2 - Matching: basic_int: matches: matches expectation: 42 +ok 3 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +ok 4 - Matching: basic_string: matches: matches expectation: "this is a test" diff --git a/testdata/matching_transformers.0.documentation b/testdata/matching_transformers.0.documentation new file mode 100644 index 000000000..0bce31365 --- /dev/null +++ b/testdata/matching_transformers.0.documentation @@ -0,0 +1,15 @@ +Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"wtf"}},{"not":{"match-regexp":"^ERROR:"}}]} +Matching: test_array: matches: matches expectation: [{"contain-element":{"match-regexp":"4."}},"45",{"and":[{"ge":46},{"le":50}]}] +Matching: test_gjson_transform: matches: matches expectation: {"gjson":{"@this":{"have-key":"foo"},"count":{"le":25},"foo":{"have-prefix":"b"},"moo":{"and":[{"have-key":"nested"},{"not":{"have-key":"nested2"}}]},"moo.nested":"cow"}} +Matching: test_gjson_using_this_and_equal: matches: matches expectation: {"gjson":{"@this":{"equal":{"baz":"bing","foo":"bar"}}}} +Matching: test_numeric_string: matches: matches expectation: {"and":["128",{"have-prefix":"1"},{"have-suffix":"8"},{"match-regexp":"\\d{3}"}]} +Matching: test_reader_as_single_string: matches: matches expectation: "cool" +Matching: test_reader_using_array: matches: matches expectation: ["foo bar","15","moo cow"] +Matching: test_reader_using_int_matchers: matches: matches expectation: {"and":[{"le":250},{"ge":20}]} +Matching: test_reader_using_string_matchers: matches: matches expectation: {"and":[{"have-len":19},"foo bar\n15\nmoo cow\n",{"have-prefix":"foo"},{"have-suffix":"cow\n"},{"contain-element":{"have-prefix":"moo"}},{"contain-elements":[{"not":"this_doesnt_exist"},{"lt":20},{"have-prefix":"moo"}]}]} +Matching: test_string_float: matches: matches expectation: {"and":[128.3,{"le":129},{"gt":120.2}]} +Matching: test_string_numeric: matches: matches expectation: {"and":[128,128,{"le":128},{"gt":120}]} + + +Total Duration: +Count: 11, Failed: 0, Skipped: 0 diff --git a/testdata/matching_transformers.0.nagios b/testdata/matching_transformers.0.nagios new file mode 100644 index 000000000..cc65d78fd --- /dev/null +++ b/testdata/matching_transformers.0.nagios @@ -0,0 +1 @@ +GOSS OK - Count: 11, Failed: 0, Skipped: 0, Duration: diff --git a/testdata/matching_transformers.0.rspecish b/testdata/matching_transformers.0.rspecish new file mode 100644 index 000000000..e4624df65 --- /dev/null +++ b/testdata/matching_transformers.0.rspecish @@ -0,0 +1,4 @@ +........... + +Total Duration: +Count: 11, Failed: 0, Skipped: 0 diff --git a/testdata/matching_transformers.0.tap b/testdata/matching_transformers.0.tap new file mode 100644 index 000000000..28a2d5921 --- /dev/null +++ b/testdata/matching_transformers.0.tap @@ -0,0 +1,12 @@ +1..11 +ok 1 - Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"wtf"}},{"not":{"match-regexp":"^ERROR:"}}]} +ok 2 - Matching: test_array: matches: matches expectation: [{"contain-element":{"match-regexp":"4."}},"45",{"and":[{"ge":46},{"le":50}]}] +ok 3 - Matching: test_gjson_transform: matches: matches expectation: {"gjson":{"@this":{"have-key":"foo"},"count":{"le":25},"foo":{"have-prefix":"b"},"moo":{"and":[{"have-key":"nested"},{"not":{"have-key":"nested2"}}]},"moo.nested":"cow"}} +ok 4 - Matching: test_gjson_using_this_and_equal: matches: matches expectation: {"gjson":{"@this":{"equal":{"baz":"bing","foo":"bar"}}}} +ok 5 - Matching: test_numeric_string: matches: matches expectation: {"and":["128",{"have-prefix":"1"},{"have-suffix":"8"},{"match-regexp":"\\d{3}"}]} +ok 6 - Matching: test_reader_as_single_string: matches: matches expectation: "cool" +ok 7 - Matching: test_reader_using_array: matches: matches expectation: ["foo bar","15","moo cow"] +ok 8 - Matching: test_reader_using_int_matchers: matches: matches expectation: {"and":[{"le":250},{"ge":20}]} +ok 9 - Matching: test_reader_using_string_matchers: matches: matches expectation: {"and":[{"have-len":19},"foo bar\n15\nmoo cow\n",{"have-prefix":"foo"},{"have-suffix":"cow\n"},{"contain-element":{"have-prefix":"moo"}},{"contain-elements":[{"not":"this_doesnt_exist"},{"lt":20},{"have-prefix":"moo"}]}]} +ok 10 - Matching: test_string_float: matches: matches expectation: {"and":[128.3,{"le":129},{"gt":120.2}]} +ok 11 - Matching: test_string_numeric: matches: matches expectation: {"and":[128,128,{"le":128},{"gt":120}]} diff --git a/testdata/matching_transformers.yaml b/testdata/matching_transformers.yaml new file mode 100644 index 000000000..8fc42a00e --- /dev/null +++ b/testdata/matching_transformers.yaml @@ -0,0 +1,119 @@ +matching: + basic_reader_as_array: + as-reader: true + content: | + foo bar + moo cow + matches: + and: + - contain-element: {contain-substring: 'foo'} + - contain-element: {match-regexp: '^m.*w$'} + - not: {contain-substring: 'wtf'} + - not: {match-regexp: '^ERROR:'} + + test_numeric_string: + content: 128 + matches: + and: + - '128' + - have-prefix: '1' + - have-suffix: '8' + - match-regexp: '\d{3}' + + test_string_numeric: + content: '128' + matches: + and: + - 128 + - 128.0 + - le: 128 + - gt: 120 + + test_string_float: + content: '128.3' + matches: + and: + - 128.3 + - le: 129 + - gt: 120.2 + + test_array: + content: + - '45' + - '46' + - '47' + matches: + - contain-element: {match-regexp: "4."} + - '45' + - and: [{ge: 46}, {le: 50}] + + test_reader_using_string_matchers: + content: | + foo bar + 15 + moo cow + as-reader: true + matches: + and: + - have-len: 19 + - | + foo bar + 15 + moo cow + - have-prefix: 'foo' + - have-suffix: "cow\n" + - contain-element: + have-prefix: 'moo' + - contain-elements: + - not: 'this_doesnt_exist' + - lt: 20 + - have-prefix: 'moo' + + test_reader_using_array: + content: | + foo bar + 15 + moo cow + as-reader: true + matches: + - "foo bar" + - "15" + - "moo cow" + + + test_reader_as_single_string: + content: 'cool' + as-reader: true + matches: 'cool' + + test_reader_using_int_matchers: + content: '40' + as-reader: true + matches: + and: + - le: 250 + - ge: 20 + + + test_gjson_transform: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + moo.nested: cow + foo: {have-prefix: b} + count: {le: 25} + '@this': {have-key: "foo"} + moo: + and: + - {have-key: "nested"} + - {not: {have-key: "nested2"}} + + test_gjson_using_this_and_equal: + content: '{"foo": "bar", "baz": "bing"}' + matches: + gjson: + '@this': + equal: + foo: bar + baz: bing diff --git a/testdata/matching_transformers_failing.1.documentation b/testdata/matching_transformers_failing.1.documentation new file mode 100644 index 000000000..7e208f4f2 --- /dev/null +++ b/testdata/matching_transformers_failing.1.documentation @@ -0,0 +1,223 @@ +Matching: basic_reader_as_array: matches: +Expected + ["foo bar","moo cow",""] +to contain element matching + {"contain-substring":"fox"} +the transform chain was + [{"to-array":{}}] +Matching: test_array: matches: +Expected + ["45","46","47"] +to contain elements + [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +the missing elements were + [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +Matching: test_gjson_transform_nested_and: matches: +Expected + {"nested":"cow"} +to have key matching + "nope" +the transform chain was + [{"gjson":{"Path":"moo"}}] +Matching: test_gjson_transform_nested_count: matches: +Expected + 15 +to be <= + 10 +the transform chain was + [{"gjson":{"Path":"count"}},{"to-numeric":{}}] +Matching: test_gjson_transform_nested_prefix: matches: +Expected + "bar" +to have prefix + "x" +the transform chain was + [{"gjson":{"Path":"foo"}}] +Matching: test_gjson_transform_nested_this: matches: +Expected + {"count":"15","foo":"bar","moo":{"nested":"cow"}} +to have key matching + "nope" +the transform chain was + [{"gjson":{"Path":"@this"}}] +Matching: test_gjson_transform_not_key: matches: +Expected + {"nested":"cow"} +not to have key matching + "nested" +the transform chain was + [{"gjson":{"Path":"moo"}}] +Matching: test_gjson_transform_simple: matches: +Expected + "cow" +to equal + "cowx" +the transform chain was + [{"gjson":{"Path":"moo.nested"}}] +Matching: test_gjson_using_this_and_equal: matches: +Expected + {"baz":"bing","foo":"bar"} +to equal + {"baz":"bing","fox":"bar"} +the transform chain was + [{"gjson":{"Path":"@this"}}] +Matching: test_numeric_string: matches: +Expected + "128" +to equal + "129" +the transform chain was + [{"to-string":{}}] +Matching: test_reader_as_single_string: matches: +Expected + "cool" +to equal + "not-cool" +Matching: test_reader_using_int_matchers: matches: +Expected + 40 +to be <= + 20 +the transform chain was + [{"to-numeric":{}}] +Matching: test_reader_using_string_matchers: matches: +Expected + "foo bar\n15\nmoo cow\n" +to have length + 15 +Matching: test_string_float: matches: +Expected + 128.3 +to be == + 129.3 +the transform chain was + [{"to-numeric":{}}] +Matching: test_string_numeric: matches: +Expected + 128 +to be == + 129 +the transform chain was + [{"to-numeric":{}}] + + +Failures/Skipped: + +Matching: basic_reader_as_array: matches: +Expected + ["foo bar","moo cow",""] +to contain element matching + {"contain-substring":"fox"} +the transform chain was + [{"to-array":{}}] + +Matching: test_array: matches: +Expected + ["45","46","47"] +to contain elements + [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +the missing elements were + [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] + +Matching: test_gjson_transform_nested_and: matches: +Expected + {"nested":"cow"} +to have key matching + "nope" +the transform chain was + [{"gjson":{"Path":"moo"}}] + +Matching: test_gjson_transform_nested_count: matches: +Expected + 15 +to be <= + 10 +the transform chain was + [{"gjson":{"Path":"count"}},{"to-numeric":{}}] + +Matching: test_gjson_transform_nested_prefix: matches: +Expected + "bar" +to have prefix + "x" +the transform chain was + [{"gjson":{"Path":"foo"}}] + +Matching: test_gjson_transform_nested_this: matches: +Expected + {"count":"15","foo":"bar","moo":{"nested":"cow"}} +to have key matching + "nope" +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_gjson_transform_not_key: matches: +Expected + {"nested":"cow"} +not to have key matching + "nested" +the transform chain was + [{"gjson":{"Path":"moo"}}] + +Matching: test_gjson_transform_simple: matches: +Expected + "cow" +to equal + "cowx" +the transform chain was + [{"gjson":{"Path":"moo.nested"}}] + +Matching: test_gjson_using_this_and_equal: matches: +Expected + {"baz":"bing","foo":"bar"} +to equal + {"baz":"bing","fox":"bar"} +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_numeric_string: matches: +Expected + "128" +to equal + "129" +the transform chain was + [{"to-string":{}}] + +Matching: test_reader_as_single_string: matches: +Expected + "cool" +to equal + "not-cool" + +Matching: test_reader_using_int_matchers: matches: +Expected + 40 +to be <= + 20 +the transform chain was + [{"to-numeric":{}}] + +Matching: test_reader_using_string_matchers: matches: +Expected + "foo bar\n15\nmoo cow\n" +to have length + 15 + +Matching: test_string_float: matches: +Expected + 128.3 +to be == + 129.3 +the transform chain was + [{"to-numeric":{}}] + +Matching: test_string_numeric: matches: +Expected + 128 +to be == + 129 +the transform chain was + [{"to-numeric":{}}] + +Total Duration: +Count: 15, Failed: 15, Skipped: 0 diff --git a/testdata/matching_transformers_failing.1.rspecish b/testdata/matching_transformers_failing.1.rspecish new file mode 100644 index 000000000..e790564db --- /dev/null +++ b/testdata/matching_transformers_failing.1.rspecish @@ -0,0 +1,122 @@ +FFFFFFFFFFFFFFF + +Failures/Skipped: + +Matching: basic_reader_as_array: matches: +Expected + ["foo bar","moo cow",""] +to contain element matching + {"contain-substring":"fox"} +the transform chain was + [{"to-array":{}}] + +Matching: test_array: matches: +Expected + ["45","46","47"] +to contain elements + [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +the missing elements were + [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] + +Matching: test_gjson_transform_nested_and: matches: +Expected + {"nested":"cow"} +to have key matching + "nope" +the transform chain was + [{"gjson":{"Path":"moo"}}] + +Matching: test_gjson_transform_nested_count: matches: +Expected + 15 +to be <= + 10 +the transform chain was + [{"gjson":{"Path":"count"}},{"to-numeric":{}}] + +Matching: test_gjson_transform_nested_prefix: matches: +Expected + "bar" +to have prefix + "x" +the transform chain was + [{"gjson":{"Path":"foo"}}] + +Matching: test_gjson_transform_nested_this: matches: +Expected + {"count":"15","foo":"bar","moo":{"nested":"cow"}} +to have key matching + "nope" +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_gjson_transform_not_key: matches: +Expected + {"nested":"cow"} +not to have key matching + "nested" +the transform chain was + [{"gjson":{"Path":"moo"}}] + +Matching: test_gjson_transform_simple: matches: +Expected + "cow" +to equal + "cowx" +the transform chain was + [{"gjson":{"Path":"moo.nested"}}] + +Matching: test_gjson_using_this_and_equal: matches: +Expected + {"baz":"bing","foo":"bar"} +to equal + {"baz":"bing","fox":"bar"} +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_numeric_string: matches: +Expected + "128" +to equal + "129" +the transform chain was + [{"to-string":{}}] + +Matching: test_reader_as_single_string: matches: +Expected + "cool" +to equal + "not-cool" + +Matching: test_reader_using_int_matchers: matches: +Expected + 40 +to be <= + 20 +the transform chain was + [{"to-numeric":{}}] + +Matching: test_reader_using_string_matchers: matches: +Expected + "foo bar\n15\nmoo cow\n" +to have length + 15 + +Matching: test_string_float: matches: +Expected + 128.3 +to be == + 129.3 +the transform chain was + [{"to-numeric":{}}] + +Matching: test_string_numeric: matches: +Expected + 128 +to be == + 129 +the transform chain was + [{"to-numeric":{}}] + +Total Duration: +Count: 15, Failed: 15, Skipped: 0 diff --git a/testdata/matching_transformers_failing.1.tap b/testdata/matching_transformers_failing.1.tap new file mode 100644 index 000000000..d40fa100c --- /dev/null +++ b/testdata/matching_transformers_failing.1.tap @@ -0,0 +1,16 @@ +1..15 +not ok 1 - Matching: basic_reader_as_array: matches: Expected ["foo bar","moo cow",""] to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] +not ok 2 - Matching: test_array: matches: Expected ["45","46","47"] to contain elements [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +not ok 3 - Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] +not ok 4 - Matching: test_gjson_transform_nested_count: matches: Expected 15 to be <= 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] +not ok 5 - Matching: test_gjson_transform_nested_prefix: matches: Expected "bar" to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] +not ok 6 - Matching: test_gjson_transform_nested_this: matches: Expected {"count":"15","foo":"bar","moo":{"nested":"cow"}} to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] +not ok 7 - Matching: test_gjson_transform_not_key: matches: Expected {"nested":"cow"} not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] +not ok 8 - Matching: test_gjson_transform_simple: matches: Expected "cow" to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] +not ok 9 - Matching: test_gjson_using_this_and_equal: matches: Expected {"baz":"bing","foo":"bar"} to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] +not ok 10 - Matching: test_numeric_string: matches: Expected "128" to equal "129" the transform chain was [{"to-string":{}}] +not ok 11 - Matching: test_reader_as_single_string: matches: Expected "cool" to equal "not-cool" +not ok 12 - Matching: test_reader_using_int_matchers: matches: Expected 40 to be <= 20 the transform chain was [{"to-numeric":{}}] +not ok 13 - Matching: test_reader_using_string_matchers: matches: Expected "foo bar\n15\nmoo cow\n" to have length 15 +not ok 14 - Matching: test_string_float: matches: Expected 128.3 to be == 129.3 the transform chain was [{"to-numeric":{}}] +not ok 15 - Matching: test_string_numeric: matches: Expected 128 to be == 129 the transform chain was [{"to-numeric":{}}] diff --git a/testdata/matching_transformers_failing.2.nagios b/testdata/matching_transformers_failing.2.nagios new file mode 100644 index 000000000..8792c3bc4 --- /dev/null +++ b/testdata/matching_transformers_failing.2.nagios @@ -0,0 +1 @@ +GOSS CRITICAL - Count: 15, Failed: 15, Skipped: 0, Duration: diff --git a/testdata/matching_transformers_failing.yaml b/testdata/matching_transformers_failing.yaml new file mode 100644 index 000000000..cca70e7f2 --- /dev/null +++ b/testdata/matching_transformers_failing.yaml @@ -0,0 +1,141 @@ +matching: + basic_reader_as_array: + as-reader: true + content: | + foo bar + moo cow + matches: + and: + - contain-element: {contain-substring: 'fox'} + - contain-element: {match-regexp: '^t.*w$'} + - not: {contain-substring: 'foo'} + - not: {match-regexp: '^foo'} + + test_numeric_string: + content: 128 + matches: + and: + - '129' + - have-prefix: '2' + - have-suffix: '9' + - match-regexp: '\s{3}' + + test_string_numeric: + content: '128' + matches: + and: + - 129 + - 129.1 + - le: 127 + - gt: 130 + + test_string_float: + content: '128.3' + matches: + and: + - 129.3 + - le: 127 + - gt: 130.2 + + test_array: + content: + - '45' + - '46' + - '47' + matches: + - contain-element: {match-regexp: "5."} + - '55' + - and: [{ge: 56}, {le: 30}] + + test_reader_using_string_matchers: + content: | + foo bar + 15 + moo cow + as-reader: true + matches: + and: + - have-len: 15 + - | + fox bar + 15 + moo cow + - have-prefix: 'fox' + - have-suffix: "tow\n" + - contain-element: + have-prefix: 'too' + - contain-elements: + - not: 'moo cow' + - lt: 10 + - have-prefix: 'tow' + + + test_reader_as_single_string: + content: 'cool' + as-reader: true + matches: 'not-cool' + + test_reader_using_int_matchers: + content: '40' + as-reader: true + matches: + and: + - le: 20 + - ge: 50 + + + test_gjson_transform_simple: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + moo.nested: cowx + + test_gjson_transform_nested_prefix: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + foo: {have-prefix: x} + + test_gjson_transform_nested_count: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + count: {le: 10} + + test_gjson_transform_nested_this: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + '@this': {have-key: "nope"} + + test_gjson_transform_nested_and: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + moo: + and: + - {have-key: "nope"} + - {not: {have-key: "nested"}} + + test_gjson_transform_not_key: + content: '{"foo": "bar", "moo": {"nested": "cow"}, "count": "15"}' + as-reader: true + matches: + gjson: + moo: + not: + have-key: "nested" + + test_gjson_using_this_and_equal: + content: '{"foo": "bar", "baz": "bing"}' + matches: + gjson: + '@this': + equal: + fox: bar + baz: bing diff --git a/validate.go b/validate.go index 5a9fab375..8505d217e 100644 --- a/validate.go +++ b/validate.go @@ -101,6 +101,14 @@ func ValidateResults(c *util.Config) (results <-chan []resource.TestResult, err // by the typical CLI invocation and will produce output to StdOut. Use // ValidateResults for programmatic access func Validate(c *util.Config) (code int, err error) { + gossConfig, err := getGossConfig(c.Vars, c.VarsInline, c.Spec) + if err != nil { + return 78, err + } + return ValidateConfig(c, gossConfig) +} + +func ValidateConfig(c *util.Config, gossConfig *GossConfig) (code int, err error) { // Needed for contains-elements // Maybe we don't use this and use custom // contain_element_matcher is needed because it's single entry to avoid @@ -110,11 +118,6 @@ func Validate(c *util.Config) (code int, err error) { FormatOptions: c.FormatOptions, } - gossConfig, err := getGossConfig(c.Vars, c.VarsInline, c.Spec) - if err != nil { - return 78, err - } - sys := system.New(c.PackageManager) outputer, err := getOutputer(c.NoColor, c.OutputFormat) if err != nil { From 5baad1421de7f2bc2651487a27d0c32da54ee6ce Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Fri, 2 Jun 2023 18:06:12 -0700 Subject: [PATCH 25/41] Increase test coverage --- matcher_test.go | 84 ++++++++++++ matchers/and.go | 6 +- matchers/equal_matcher.go | 3 + matchers/matchers.go | 3 +- matchers/type_conversion.go | 30 ++--- outputs/outputs.go | 2 +- testdata/matching_basic.0.nagios | 1 - testdata/matching_basic.0.tap | 14 -- testdata/matching_basic.1.documentation | 27 ---- testdata/matching_basic.1.rspecish | 12 -- testdata/matching_basic.1.tap | 14 -- testdata/matching_basic.2.nagios | 1 - testdata/matching_basic.78.documentation | 0 testdata/matching_basic.78.nagios | 0 testdata/matching_basic.78.rspecish | 0 testdata/matching_basic.78.tap | 0 testdata/matching_basic.yaml | 9 +- testdata/matching_basic_failing.1.tap | 15 --- testdata/matching_basic_failing.2.nagios | 1 - testdata/matching_basic_failing.yaml | 67 +++++++++ .../matching_basic_string.0.documentation | 8 -- testdata/matching_basic_string.0.nagios | 1 - testdata/matching_basic_string.0.rspecish | 4 - testdata/matching_basic_string.0.tap | 5 - testdata/matching_transformers.0.nagios | 1 - testdata/matching_transformers.0.rspecish | 4 - testdata/matching_transformers.0.tap | 12 -- testdata/matching_transformers.yaml | 12 +- testdata/matching_transformers_failing.1.tap | 16 --- .../matching_transformers_failing.2.nagios | 1 - testdata/matching_transformers_failing.yaml | 21 +++ ...ion => out_matching_basic.0.documentation} | 7 +- testdata/out_matching_basic.0.nagios | 1 + ...rspecish => out_matching_basic.0.rspecish} | 4 +- testdata/out_matching_basic.0.tap | 15 +++ ...ut_matching_basic_failing.1.documentation} | 127 +++++++++++++++++- ... => out_matching_basic_failing.1.rspecish} | 72 +++++++++- testdata/out_matching_basic_failing.1.tap | 26 ++++ testdata/out_matching_basic_failing.2.nagios | 1 + ...out_matching_transformers.0.documentation} | 3 +- testdata/out_matching_transformers.0.nagios | 1 + testdata/out_matching_transformers.0.rspecish | 4 + testdata/out_matching_transformers.0.tap | 13 ++ ...hing_transformers_failing.1.documentation} | 39 +++++- ..._matching_transformers_failing.1.rspecish} | 24 +++- .../out_matching_transformers_failing.1.tap | 19 +++ ...out_matching_transformers_failing.2.nagios | 1 + 47 files changed, 556 insertions(+), 175 deletions(-) create mode 100644 matcher_test.go delete mode 100644 testdata/matching_basic.0.nagios delete mode 100644 testdata/matching_basic.0.tap delete mode 100644 testdata/matching_basic.1.documentation delete mode 100644 testdata/matching_basic.1.rspecish delete mode 100644 testdata/matching_basic.1.tap delete mode 100644 testdata/matching_basic.2.nagios delete mode 100644 testdata/matching_basic.78.documentation delete mode 100644 testdata/matching_basic.78.nagios delete mode 100644 testdata/matching_basic.78.rspecish delete mode 100644 testdata/matching_basic.78.tap delete mode 100644 testdata/matching_basic_failing.1.tap delete mode 100644 testdata/matching_basic_failing.2.nagios delete mode 100644 testdata/matching_basic_string.0.documentation delete mode 100644 testdata/matching_basic_string.0.nagios delete mode 100644 testdata/matching_basic_string.0.rspecish delete mode 100644 testdata/matching_basic_string.0.tap delete mode 100644 testdata/matching_transformers.0.nagios delete mode 100644 testdata/matching_transformers.0.rspecish delete mode 100644 testdata/matching_transformers.0.tap delete mode 100644 testdata/matching_transformers_failing.1.tap delete mode 100644 testdata/matching_transformers_failing.2.nagios rename testdata/{matching_basic.0.documentation => out_matching_basic.0.documentation} (81%) create mode 100644 testdata/out_matching_basic.0.nagios rename testdata/{matching_basic.0.rspecish => out_matching_basic.0.rspecish} (63%) create mode 100644 testdata/out_matching_basic.0.tap rename testdata/{matching_basic_failing.1.documentation => out_matching_basic_failing.1.documentation} (64%) rename testdata/{matching_basic_failing.1.rspecish => out_matching_basic_failing.1.rspecish} (63%) create mode 100644 testdata/out_matching_basic_failing.1.tap create mode 100644 testdata/out_matching_basic_failing.2.nagios rename testdata/{matching_transformers.0.documentation => out_matching_transformers.0.documentation} (90%) create mode 100644 testdata/out_matching_transformers.0.nagios create mode 100644 testdata/out_matching_transformers.0.rspecish create mode 100644 testdata/out_matching_transformers.0.tap rename testdata/{matching_transformers_failing.1.documentation => out_matching_transformers_failing.1.documentation} (81%) rename testdata/{matching_transformers_failing.1.rspecish => out_matching_transformers_failing.1.rspecish} (80%) create mode 100644 testdata/out_matching_transformers_failing.1.tap create mode 100644 testdata/out_matching_transformers_failing.2.nagios diff --git a/matcher_test.go b/matcher_test.go new file mode 100644 index 000000000..5abd96bbd --- /dev/null +++ b/matcher_test.go @@ -0,0 +1,84 @@ +package goss + +import ( + "bytes" + "flag" + "fmt" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "testing" + + "github.com/aelsabbahy/goss/util" + "github.com/stretchr/testify/assert" +) + +var ( + update = flag.Bool("update", false, "update the golden files of this test") +) + +func TestMain(m *testing.M) { + flag.Parse() + os.Exit(m.Run()) +} + +func TestMatchers(t *testing.T) { + files, err := filepath.Glob("testdata/out_matching_*") + if err != nil { + t.Fatal(err) + } + + for _, outFile := range files { + outFile := outFile + parts := strings.Split(outFile, ".") + tf := fmt.Sprintf("testdata/%s.yaml", strings.TrimPrefix(parts[0], "testdata/out_")) + outFormat := parts[2] + wantCode, err := strconv.Atoi(parts[1]) + if err != nil { + t.Fatal(err) + } + //tn := tf + " " + outFormat + wantCode + tn := outFile + t.Run(tn, func(t *testing.T) { + output := &bytes.Buffer{} + + cfg, err := util.NewConfig( + util.WithOutputFormat(outFormat), + util.WithResultWriter(output), + util.WithSpecFile(tf), + util.WithFormatOptions("sort", "pretty"), + ) + if err != nil { + t.Fatal(err) + } + exitCode, err := Validate(cfg) + actualOut := output.String() + actualOut = sanitizeOutput(actualOut) + + //outFile := strings.TrimSuffix(tf, filepath.Ext(tf)) + //outFile = fmt.Sprintf("%s.%d.%s", outFile, exitCode, of) + if *update { + os.WriteFile(outFile, []byte(actualOut), 0644) + } + wantOutB, err := os.ReadFile(outFile) + if err != nil { + t.Fatal(err) + } + wantOut := string(wantOutB) + if actualOut != wantOut { + assert.Equal(t, wantOut, actualOut) + } + if exitCode != wantCode { + assert.Equal(t, wantCode, exitCode) + } + }) + } +} + +func sanitizeOutput(s string) string { + // Remove duration time + re := regexp.MustCompile(`\d\.\d\d\ds`) + return re.ReplaceAllString(s, "") +} diff --git a/matchers/and.go b/matchers/and.go index 463a6dbc1..12aad5e3b 100644 --- a/matchers/and.go +++ b/matchers/and.go @@ -2,7 +2,6 @@ package matchers import ( "encoding/json" - "fmt" ) type AndMatcher struct { @@ -35,8 +34,9 @@ func (m *AndMatcher) FailureResult(actual interface{}) MatcherResult { func (m *AndMatcher) NegatedFailureResult(actual interface{}) MatcherResult { return MatcherResult{ - Actual: actual, - Message: fmt.Sprintf("To not satisfy all of these matchers: %s", m.Matchers), + Actual: actual, + Message: "To not satisfy all of these matchers", + Expected: m.Matchers, } } diff --git a/matchers/equal_matcher.go b/matchers/equal_matcher.go index faa2125ea..8fe5ef273 100644 --- a/matchers/equal_matcher.go +++ b/matchers/equal_matcher.go @@ -36,4 +36,7 @@ func (m *EqualMatcher) NegatedFailureResult(actual interface{}) MatcherResult { func (m *EqualMatcher) MarshalJSON() ([]byte, error) { return json.Marshal(m.Expected) + j := make(map[string]interface{}) + j["equal"] = m.Expected + return json.Marshal(j) } diff --git a/matchers/matchers.go b/matchers/matchers.go index 1d03d498e..aba016e09 100644 --- a/matchers/matchers.go +++ b/matchers/matchers.go @@ -1,7 +1,6 @@ package matchers import ( - "encoding/json" "reflect" "unsafe" @@ -14,7 +13,7 @@ type GossMatcher interface { //Match(actual interface{}) (success bool, err error) FailureResult(actual interface{}) MatcherResult NegatedFailureResult(actual interface{}) MatcherResult - json.Marshaler + //json.Marshaler } type MatcherResult struct { diff --git a/matchers/type_conversion.go b/matchers/type_conversion.go index 0999ef49f..0d425bb4f 100644 --- a/matchers/type_conversion.go +++ b/matchers/type_conversion.go @@ -84,21 +84,21 @@ func (matcher ToArray) MarshalJSON() ([]byte, error) { return json.Marshal(j) } -type ReaderToStrings struct{} - -func (t ReaderToStrings) Transform(i interface{}) (interface{}, error) { - r, ok := i.(io.Reader) - if !ok { - return nil, fmt.Errorf("Expected io.reader, Got:%s", format.Object(i, 1)) - } - var lines []string - i, err := ReaderToString{}.Transform(r) - if err != nil { - return lines, err - } - s := i.(string) - return strings.Split(s, "\n"), nil -} +//type ReaderToStrings struct{} +// +//func (t ReaderToStrings) Transform(i interface{}) (interface{}, error) { +// r, ok := i.(io.Reader) +// if !ok { +// return nil, fmt.Errorf("Expected io.reader, Got:%s", format.Object(i, 1)) +// } +// var lines []string +// i, err := ReaderToString{}.Transform(r) +// if err != nil { +// return lines, err +// } +// s := i.(string) +// return strings.Split(s, "\n"), nil +//} type ReaderToString struct{} diff --git a/outputs/outputs.go b/outputs/outputs.go index db152ff18..40c2a097e 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -123,11 +123,11 @@ func prettyPrint(i interface{}, indent bool) string { encoder := json.NewEncoder(buffer) encoder.SetEscapeHTML(false) var b []byte + // []matchers.GossMatcher doesn't print correctly err := encoder.Encode(i) if err == nil { b = buffer.Bytes() } else { - //b = []byte(fmt.Sprint(err)) b = []byte(fmt.Sprint(i)) } b = bytes.TrimRightFunc(b, unicode.IsSpace) diff --git a/testdata/matching_basic.0.nagios b/testdata/matching_basic.0.nagios deleted file mode 100644 index b3d7a9e78..000000000 --- a/testdata/matching_basic.0.nagios +++ /dev/null @@ -1 +0,0 @@ -GOSS OK - Count: 13, Failed: 0, Skipped: 1, Duration: diff --git a/testdata/matching_basic.0.tap b/testdata/matching_basic.0.tap deleted file mode 100644 index 583a03b12..000000000 --- a/testdata/matching_basic.0.tap +++ /dev/null @@ -1,14 +0,0 @@ -1..13 -ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] -ok 2 - Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} -ok 3 - Matching: basic_int: matches: matches expectation: 42 -ok 4 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] -ok 5 - Matching: basic_string: matches: matches expectation: "this is a test" -ok 6 - Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} -ok 7 - # SKIP Matching: basic_string_skip: matches: skipped -ok 8 - Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} -ok 9 - Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} -ok 10 - Matching: negated_basic_int: matches: matches expectation: {"not":43} -ok 11 - Matching: negated_basic_reader: matches: matches expectation: {"not":{"contain-elements":["fox","/^t.*w$/","!foo","!/^foo/"]}} -ok 12 - Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} -ok 13 - Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} diff --git a/testdata/matching_basic.1.documentation b/testdata/matching_basic.1.documentation deleted file mode 100644 index 03d5dcbc1..000000000 --- a/testdata/matching_basic.1.documentation +++ /dev/null @@ -1,27 +0,0 @@ -Matching: basic_array: matches: matches expectation: ["group1","group2"] -Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} -Matching: basic_int: matches: matches expectation: 42 -Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] -Matching: basic_string: matches: matches expectation: "this is a test" -Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} -Matching: basic_string_skip: matches: skipped -Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} -Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} -Matching: negated_basic_int: matches: matches expectation: {"not":43} -Matching: negated_basic_reader: matches: -Error - Unknown matcher: contains-elements -Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} -Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} - - -Failures/Skipped: - -Matching: basic_string_skip: matches: skipped - -Matching: negated_basic_reader: matches: -Error - Unknown matcher: contains-elements - -Total Duration: -Count: 13, Failed: 1, Skipped: 1 diff --git a/testdata/matching_basic.1.rspecish b/testdata/matching_basic.1.rspecish deleted file mode 100644 index 523a589b1..000000000 --- a/testdata/matching_basic.1.rspecish +++ /dev/null @@ -1,12 +0,0 @@ -SF........... - -Failures/Skipped: - -Matching: basic_string_skip: matches: skipped - -Matching: negated_basic_reader: matches: -Error - Unknown matcher: contains-elements - -Total Duration: -Count: 13, Failed: 1, Skipped: 1 diff --git a/testdata/matching_basic.1.tap b/testdata/matching_basic.1.tap deleted file mode 100644 index 7fa444668..000000000 --- a/testdata/matching_basic.1.tap +++ /dev/null @@ -1,14 +0,0 @@ -1..13 -ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] -ok 2 - Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} -ok 3 - Matching: basic_int: matches: matches expectation: 42 -ok 4 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] -ok 5 - Matching: basic_string: matches: matches expectation: "this is a test" -ok 6 - Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} -ok 7 - # SKIP Matching: basic_string_skip: matches: skipped -ok 8 - Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} -ok 9 - Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} -ok 10 - Matching: negated_basic_int: matches: matches expectation: {"not":43} -not ok 11 - Matching: negated_basic_reader: matches: Error Unknown matcher: contains-elements -ok 12 - Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} -ok 13 - Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} diff --git a/testdata/matching_basic.2.nagios b/testdata/matching_basic.2.nagios deleted file mode 100644 index 3d0c16687..000000000 --- a/testdata/matching_basic.2.nagios +++ /dev/null @@ -1 +0,0 @@ -GOSS CRITICAL - Count: 13, Failed: 1, Skipped: 1, Duration: diff --git a/testdata/matching_basic.78.documentation b/testdata/matching_basic.78.documentation deleted file mode 100644 index e69de29bb..000000000 diff --git a/testdata/matching_basic.78.nagios b/testdata/matching_basic.78.nagios deleted file mode 100644 index e69de29bb..000000000 diff --git a/testdata/matching_basic.78.rspecish b/testdata/matching_basic.78.rspecish deleted file mode 100644 index e69de29bb..000000000 diff --git a/testdata/matching_basic.78.tap b/testdata/matching_basic.78.tap deleted file mode 100644 index e69de29bb..000000000 diff --git a/testdata/matching_basic.yaml b/testdata/matching_basic.yaml index 323ad3f0a..ebc0e2b34 100644 --- a/testdata/matching_basic.yaml +++ b/testdata/matching_basic.yaml @@ -14,6 +14,11 @@ matching: content: 'this is a test' matches: 'this is a test' + basic_semver: + content: '1.2.3' + matches: + semver-constraint: '>=1.2.0' + basic_int: content: 42 matches: 42 @@ -37,6 +42,8 @@ matching: - consist-of: [foo, have-prefix: m, bar] # order doesn't matter, can use matchers - contain-element: have-prefix: b + - contain-element: + have-suffix: r basic_reader: as-reader: true @@ -88,7 +95,7 @@ matching: - not: equal: [fox, bax, mox] # order matters, exact match - not: - consist-of: [fox, have-prefix: t, box] # order doesn't matter, can use matchers + consist-of: [have-suffix: x, have-prefix: t, box] # order doesn't matter, can use matchers - not: contain-element: have-prefix: x diff --git a/testdata/matching_basic_failing.1.tap b/testdata/matching_basic_failing.1.tap deleted file mode 100644 index 4afe4cd3e..000000000 --- a/testdata/matching_basic_failing.1.tap +++ /dev/null @@ -1,15 +0,0 @@ -1..14 -not ok 1 - Matching: basic_array: matches: Expected ["group1","group2","group3"] to contain elements ["group1","group2","group2","group4"] the missing elements were ["group2","group4"] -not ok 2 - Matching: basic_array_consists_of: matches: Expected ["foo","bar","moo"] to consist of ["fox",{"have-prefix":"t"},"box"] the missing elements were ["fox",{"have-prefix":"t"},"box"] the extra elements were ["foo","bar","moo"] -not ok 3 - Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] -not ok 4 - Matching: basic_int: matches: Expected 42 to be == 43 -not ok 5 - Matching: basic_reader: matches: Expected "object: *strings.Reader" to contain patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] -not ok 6 - Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test" -not ok 7 - Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" -not ok 8 - Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] not to contain elements ["group1","group2","group3"] -not ok 9 - Matching: negated_basic_array_consists_of: matches: Expected ["foo","bar","moo"] not to consist of ["foo",{"have-prefix":"m"},"bar"] -not ok 10 - Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] -not ok 11 - Matching: negated_basic_int: matches: Expected 42 not to be == 42 -not ok 12 - Matching: negated_basic_reader: matches: Error ContainElements matcher expects an array/slice/map. Got: : foo bar moo cow -not ok 13 - Matching: negated_basic_string: matches: Expected "this is a test" not to equal "this is a test" -not ok 14 - Matching: negatedbasic_string_regexp: matches: Expected "this is a test" not to match regular expression "^this" diff --git a/testdata/matching_basic_failing.2.nagios b/testdata/matching_basic_failing.2.nagios deleted file mode 100644 index 5cebddf7b..000000000 --- a/testdata/matching_basic_failing.2.nagios +++ /dev/null @@ -1 +0,0 @@ -GOSS CRITICAL - Count: 14, Failed: 14, Skipped: 0, Duration: diff --git a/testdata/matching_basic_failing.yaml b/testdata/matching_basic_failing.yaml index 67acc87e2..b55d13cb3 100644 --- a/testdata/matching_basic_failing.yaml +++ b/testdata/matching_basic_failing.yaml @@ -4,11 +4,38 @@ matching: content: 'this is a test' matches: 'this is a failing test' + basic_string_have_prefix: + content: 'foo' + matches: + have-prefix: 'g' + + basic_string_have_suffix: + content: 'foo' + matches: + have-suffix: 'x' + + basic_string_contain_substring: + content: 'foo' + matches: + contain-substring: 'x' + basic_string_regexp: content: 'this is a test' matches: match-regexp: '^foo' + basic_semver: + content: '1.2.3' + matches: + or: + - semver-constraint: '>=9.9.0' + + basic_len: + content: "123" + matches: + or: + - have-len: 2 + basic_int: content: 42 matches: 43 @@ -34,6 +61,8 @@ matching: - consist-of: [fox, have-prefix: t, box] # order doesn't matter, can use matchers - contain-element: have-prefix: x + - contain-element: + have-suffix: x basic_array_consists_of: content: [foo, bar, moo] @@ -63,11 +92,43 @@ matching: not: match-regexp: '^this' + negatedbasic_string_have_prefix: + content: 'foo' + matches: + not: + have-prefix: 'f' + + negatedbasic_string_have_suffix: + content: 'foo' + matches: + not: + have-suffix: 'o' + + negatedbasic_string_contain_substring: + content: 'foo' + matches: + not: + contain-substring: 'oo' + + negatedbasic_len: + content: '123' + matches: + not: + have-len: 3 + negated_basic_int: content: 42 matches: not: 42 + negated_and: + content: 42 + matches: + not: + and: + - 42 + - 42 + negated_basic_array: content: - 'group1' @@ -95,6 +156,12 @@ matching: contain-element: have-prefix: b + negated_basic_array_contain_element: + content: [foo, bar, moo] + matches: + not: + contain-element: foo + negated_basic_array_consists_of: content: [foo, bar, moo] matches: diff --git a/testdata/matching_basic_string.0.documentation b/testdata/matching_basic_string.0.documentation deleted file mode 100644 index 028bdfb3a..000000000 --- a/testdata/matching_basic_string.0.documentation +++ /dev/null @@ -1,8 +0,0 @@ -Matching: basic_array: matches: matches expectation: ["group1","group2"] -Matching: basic_int: matches: matches expectation: 42 -Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] -Matching: basic_string: matches: matches expectation: "this is a test" - - -Total Duration: 0.000s -Count: 4, Failed: 0, Skipped: 0 diff --git a/testdata/matching_basic_string.0.nagios b/testdata/matching_basic_string.0.nagios deleted file mode 100644 index 18fc27535..000000000 --- a/testdata/matching_basic_string.0.nagios +++ /dev/null @@ -1 +0,0 @@ -GOSS OK - Count: 4, Failed: 0, Skipped: 0, Duration: 0.000s diff --git a/testdata/matching_basic_string.0.rspecish b/testdata/matching_basic_string.0.rspecish deleted file mode 100644 index c76e69d36..000000000 --- a/testdata/matching_basic_string.0.rspecish +++ /dev/null @@ -1,4 +0,0 @@ -.... - -Total Duration: 0.000s -Count: 4, Failed: 0, Skipped: 0 diff --git a/testdata/matching_basic_string.0.tap b/testdata/matching_basic_string.0.tap deleted file mode 100644 index 29923fbc5..000000000 --- a/testdata/matching_basic_string.0.tap +++ /dev/null @@ -1,5 +0,0 @@ -1..4 -ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] -ok 2 - Matching: basic_int: matches: matches expectation: 42 -ok 3 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] -ok 4 - Matching: basic_string: matches: matches expectation: "this is a test" diff --git a/testdata/matching_transformers.0.nagios b/testdata/matching_transformers.0.nagios deleted file mode 100644 index cc65d78fd..000000000 --- a/testdata/matching_transformers.0.nagios +++ /dev/null @@ -1 +0,0 @@ -GOSS OK - Count: 11, Failed: 0, Skipped: 0, Duration: diff --git a/testdata/matching_transformers.0.rspecish b/testdata/matching_transformers.0.rspecish deleted file mode 100644 index e4624df65..000000000 --- a/testdata/matching_transformers.0.rspecish +++ /dev/null @@ -1,4 +0,0 @@ -........... - -Total Duration: -Count: 11, Failed: 0, Skipped: 0 diff --git a/testdata/matching_transformers.0.tap b/testdata/matching_transformers.0.tap deleted file mode 100644 index 28a2d5921..000000000 --- a/testdata/matching_transformers.0.tap +++ /dev/null @@ -1,12 +0,0 @@ -1..11 -ok 1 - Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"wtf"}},{"not":{"match-regexp":"^ERROR:"}}]} -ok 2 - Matching: test_array: matches: matches expectation: [{"contain-element":{"match-regexp":"4."}},"45",{"and":[{"ge":46},{"le":50}]}] -ok 3 - Matching: test_gjson_transform: matches: matches expectation: {"gjson":{"@this":{"have-key":"foo"},"count":{"le":25},"foo":{"have-prefix":"b"},"moo":{"and":[{"have-key":"nested"},{"not":{"have-key":"nested2"}}]},"moo.nested":"cow"}} -ok 4 - Matching: test_gjson_using_this_and_equal: matches: matches expectation: {"gjson":{"@this":{"equal":{"baz":"bing","foo":"bar"}}}} -ok 5 - Matching: test_numeric_string: matches: matches expectation: {"and":["128",{"have-prefix":"1"},{"have-suffix":"8"},{"match-regexp":"\\d{3}"}]} -ok 6 - Matching: test_reader_as_single_string: matches: matches expectation: "cool" -ok 7 - Matching: test_reader_using_array: matches: matches expectation: ["foo bar","15","moo cow"] -ok 8 - Matching: test_reader_using_int_matchers: matches: matches expectation: {"and":[{"le":250},{"ge":20}]} -ok 9 - Matching: test_reader_using_string_matchers: matches: matches expectation: {"and":[{"have-len":19},"foo bar\n15\nmoo cow\n",{"have-prefix":"foo"},{"have-suffix":"cow\n"},{"contain-element":{"have-prefix":"moo"}},{"contain-elements":[{"not":"this_doesnt_exist"},{"lt":20},{"have-prefix":"moo"}]}]} -ok 10 - Matching: test_string_float: matches: matches expectation: {"and":[128.3,{"le":129},{"gt":120.2}]} -ok 11 - Matching: test_string_numeric: matches: matches expectation: {"and":[128,128,{"le":128},{"gt":120}]} diff --git a/testdata/matching_transformers.yaml b/testdata/matching_transformers.yaml index 8fc42a00e..0a10ea39e 100644 --- a/testdata/matching_transformers.yaml +++ b/testdata/matching_transformers.yaml @@ -106,7 +106,7 @@ matching: '@this': {have-key: "foo"} moo: and: - - {have-key: "nested"} + - have-key: "nested" - {not: {have-key: "nested2"}} test_gjson_using_this_and_equal: @@ -117,3 +117,13 @@ matching: equal: foo: bar baz: bing + + test_gjson_have_key_array: + content: '{"arr": [{"nested": "cow"}, {"nested2": "moo"}]}' + matches: + gjson: + arr: + # or tests MarshalJSON + or: + - contain-elements: + - have-key: 'nested' diff --git a/testdata/matching_transformers_failing.1.tap b/testdata/matching_transformers_failing.1.tap deleted file mode 100644 index d40fa100c..000000000 --- a/testdata/matching_transformers_failing.1.tap +++ /dev/null @@ -1,16 +0,0 @@ -1..15 -not ok 1 - Matching: basic_reader_as_array: matches: Expected ["foo bar","moo cow",""] to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] -not ok 2 - Matching: test_array: matches: Expected ["45","46","47"] to contain elements [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] -not ok 3 - Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] -not ok 4 - Matching: test_gjson_transform_nested_count: matches: Expected 15 to be <= 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] -not ok 5 - Matching: test_gjson_transform_nested_prefix: matches: Expected "bar" to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] -not ok 6 - Matching: test_gjson_transform_nested_this: matches: Expected {"count":"15","foo":"bar","moo":{"nested":"cow"}} to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] -not ok 7 - Matching: test_gjson_transform_not_key: matches: Expected {"nested":"cow"} not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] -not ok 8 - Matching: test_gjson_transform_simple: matches: Expected "cow" to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] -not ok 9 - Matching: test_gjson_using_this_and_equal: matches: Expected {"baz":"bing","foo":"bar"} to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] -not ok 10 - Matching: test_numeric_string: matches: Expected "128" to equal "129" the transform chain was [{"to-string":{}}] -not ok 11 - Matching: test_reader_as_single_string: matches: Expected "cool" to equal "not-cool" -not ok 12 - Matching: test_reader_using_int_matchers: matches: Expected 40 to be <= 20 the transform chain was [{"to-numeric":{}}] -not ok 13 - Matching: test_reader_using_string_matchers: matches: Expected "foo bar\n15\nmoo cow\n" to have length 15 -not ok 14 - Matching: test_string_float: matches: Expected 128.3 to be == 129.3 the transform chain was [{"to-numeric":{}}] -not ok 15 - Matching: test_string_numeric: matches: Expected 128 to be == 129 the transform chain was [{"to-numeric":{}}] diff --git a/testdata/matching_transformers_failing.2.nagios b/testdata/matching_transformers_failing.2.nagios deleted file mode 100644 index 8792c3bc4..000000000 --- a/testdata/matching_transformers_failing.2.nagios +++ /dev/null @@ -1 +0,0 @@ -GOSS CRITICAL - Count: 15, Failed: 15, Skipped: 0, Duration: diff --git a/testdata/matching_transformers_failing.yaml b/testdata/matching_transformers_failing.yaml index cca70e7f2..f49482ba0 100644 --- a/testdata/matching_transformers_failing.yaml +++ b/testdata/matching_transformers_failing.yaml @@ -139,3 +139,24 @@ matching: equal: fox: bar baz: bing + + test_gjson_have_key_array: + content: '{"arr": [{"nested": "cow"}, {"nested2": "moo"}]}' + matches: + gjson: + '@this': + or: + - have-key: "fail" + + test_gjson_not_found: + content: '{"arr": [{"nested": "cow"}, {"nested2": "moo"}]}' + matches: + gjson: + foo: 'bar' + + test_gjson_invalid: + content: '{"arr"' + matches: + gjson: + '@this': + - have-key: "arr" diff --git a/testdata/matching_basic.0.documentation b/testdata/out_matching_basic.0.documentation similarity index 81% rename from testdata/matching_basic.0.documentation rename to testdata/out_matching_basic.0.documentation index d7cd23535..6aca46e33 100644 --- a/testdata/matching_basic.0.documentation +++ b/testdata/out_matching_basic.0.documentation @@ -1,12 +1,13 @@ Matching: basic_array: matches: matches expectation: ["group1","group2"] -Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}}]} +Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}},{"contain-element":{"have-suffix":"r"}}]} Matching: basic_int: matches: matches expectation: 42 Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +Matching: basic_semver: matches: matches expectation: {"semver-constraint":">=1.2.0"} Matching: basic_string: matches: matches expectation: "this is a test" Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} Matching: basic_string_skip: matches: skipped Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} -Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":["fox",{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} +Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":[{"have-suffix":"x"},{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} Matching: negated_basic_int: matches: matches expectation: {"not":43} Matching: negated_basic_reader: matches: matches expectation: {"not":{"contain-elements":["fox","/^t.*w$/","!foo","!/^foo/"]}} Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} @@ -18,4 +19,4 @@ Failures/Skipped: Matching: basic_string_skip: matches: skipped Total Duration: -Count: 13, Failed: 0, Skipped: 1 +Count: 14, Failed: 0, Skipped: 1 diff --git a/testdata/out_matching_basic.0.nagios b/testdata/out_matching_basic.0.nagios new file mode 100644 index 000000000..fc2d28ea6 --- /dev/null +++ b/testdata/out_matching_basic.0.nagios @@ -0,0 +1 @@ +GOSS OK - Count: 14, Failed: 0, Skipped: 1, Duration: diff --git a/testdata/matching_basic.0.rspecish b/testdata/out_matching_basic.0.rspecish similarity index 63% rename from testdata/matching_basic.0.rspecish rename to testdata/out_matching_basic.0.rspecish index 5ef0526d5..3d8077bbf 100644 --- a/testdata/matching_basic.0.rspecish +++ b/testdata/out_matching_basic.0.rspecish @@ -1,8 +1,8 @@ -......S...... +.......S...... Failures/Skipped: Matching: basic_string_skip: matches: skipped Total Duration: -Count: 13, Failed: 0, Skipped: 1 +Count: 14, Failed: 0, Skipped: 1 diff --git a/testdata/out_matching_basic.0.tap b/testdata/out_matching_basic.0.tap new file mode 100644 index 000000000..9a1b11604 --- /dev/null +++ b/testdata/out_matching_basic.0.tap @@ -0,0 +1,15 @@ +1..14 +ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] +ok 2 - Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}},{"contain-element":{"have-suffix":"r"}}]} +ok 3 - Matching: basic_int: matches: matches expectation: 42 +ok 4 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +ok 5 - Matching: basic_semver: matches: matches expectation: {"semver-constraint":">=1.2.0"} +ok 6 - Matching: basic_string: matches: matches expectation: "this is a test" +ok 7 - Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} +ok 8 - # SKIP Matching: basic_string_skip: matches: skipped +ok 9 - Matching: negated_basic_array: matches: matches expectation: {"not":["group1","group2","group2","group4"]} +ok 10 - Matching: negated_basic_array_matchers: matches: matches expectation: {"and":[{"not":{"contain-elements":["fox","box"]}},{"not":["fox","bax"]},{"not":{"equal":["fox","bax","mox"]}},{"not":{"consist-of":[{"have-suffix":"x"},{"have-prefix":"t"},"box"]}},{"not":{"contain-element":{"have-prefix":"x"}}}]} +ok 11 - Matching: negated_basic_int: matches: matches expectation: {"not":43} +ok 12 - Matching: negated_basic_reader: matches: matches expectation: {"not":{"contain-elements":["fox","/^t.*w$/","!foo","!/^foo/"]}} +ok 13 - Matching: negated_basic_string: matches: matches expectation: {"not":"this is a failing test"} +ok 14 - Matching: negated_basic_string_regexp: matches: matches expectation: {"not":{"match-regexp":"^foo"}} diff --git a/testdata/matching_basic_failing.1.documentation b/testdata/out_matching_basic_failing.1.documentation similarity index 64% rename from testdata/matching_basic_failing.1.documentation rename to testdata/out_matching_basic_failing.1.documentation index 8136fe5fa..fb5bb733b 100644 --- a/testdata/matching_basic_failing.1.documentation +++ b/testdata/out_matching_basic_failing.1.documentation @@ -18,12 +18,17 @@ Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers - [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] + [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] Matching: basic_int: matches: Expected 42 to be == 43 +Matching: basic_len: matches: +Expected + "123" +To satisfy at least one of these matchers + [{"have-len":2}] Matching: basic_reader: matches: Expected "object: *strings.Reader" @@ -31,16 +36,41 @@ to contain patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] +Matching: basic_semver: matches: +Expected + "1.2.3" +To satisfy at least one of these matchers + [{"semver-constraint":">=9.9.0"}] Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test" +Matching: basic_string_contain_substring: matches: +Expected + "foo" +to contain substring + "x" +Matching: basic_string_have_prefix: matches: +Expected + "foo" +to have prefix + "g" +Matching: basic_string_have_suffix: matches: +Expected + "foo" +to have suffix + "x" Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" +Matching: negated_and: matches: +Expected + 42 +To not satisfy all of these matchers + [{"":42},{"":42}] Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] @@ -51,6 +81,11 @@ Expected ["foo","bar","moo"] not to consist of ["foo",{"have-prefix":"m"},"bar"] +Matching: negated_basic_array_contain_element: matches: +Expected + ["foo","bar","moo"] +to not contain element matching + "foo" Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] @@ -72,6 +107,26 @@ Expected "this is a test" not to equal "this is a test" +Matching: negatedbasic_len: matches: +Expected + "123" +not to have length + 3 +Matching: negatedbasic_string_contain_substring: matches: +Expected + "foo" +not to contain substring + "oo" +Matching: negatedbasic_string_have_prefix: matches: +Expected + "foo" +not to have prefix + "f" +Matching: negatedbasic_string_have_suffix: matches: +Expected + "foo" +not to have suffix + "o" Matching: negatedbasic_string_regexp: matches: Expected "this is a test" @@ -103,7 +158,7 @@ Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers - [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] + [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] Matching: basic_int: matches: Expected @@ -111,6 +166,12 @@ Expected to be == 43 +Matching: basic_len: matches: +Expected + "123" +To satisfy at least one of these matchers + [{"have-len":2}] + Matching: basic_reader: matches: Expected "object: *strings.Reader" @@ -119,18 +180,48 @@ to contain patterns the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] +Matching: basic_semver: matches: +Expected + "1.2.3" +To satisfy at least one of these matchers + [{"semver-constraint":">=9.9.0"}] + Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test" +Matching: basic_string_contain_substring: matches: +Expected + "foo" +to contain substring + "x" + +Matching: basic_string_have_prefix: matches: +Expected + "foo" +to have prefix + "g" + +Matching: basic_string_have_suffix: matches: +Expected + "foo" +to have suffix + "x" + Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" +Matching: negated_and: matches: +Expected + 42 +To not satisfy all of these matchers + [{"":42},{"":42}] + Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] @@ -143,6 +234,12 @@ Expected not to consist of ["foo",{"have-prefix":"m"},"bar"] +Matching: negated_basic_array_contain_element: matches: +Expected + ["foo","bar","moo"] +to not contain element matching + "foo" + Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] @@ -168,6 +265,30 @@ Expected not to equal "this is a test" +Matching: negatedbasic_len: matches: +Expected + "123" +not to have length + 3 + +Matching: negatedbasic_string_contain_substring: matches: +Expected + "foo" +not to contain substring + "oo" + +Matching: negatedbasic_string_have_prefix: matches: +Expected + "foo" +not to have prefix + "f" + +Matching: negatedbasic_string_have_suffix: matches: +Expected + "foo" +not to have suffix + "o" + Matching: negatedbasic_string_regexp: matches: Expected "this is a test" @@ -175,4 +296,4 @@ not to match regular expression "^this" Total Duration: -Count: 14, Failed: 14, Skipped: 0 +Count: 25, Failed: 25, Skipped: 0 diff --git a/testdata/matching_basic_failing.1.rspecish b/testdata/out_matching_basic_failing.1.rspecish similarity index 63% rename from testdata/matching_basic_failing.1.rspecish rename to testdata/out_matching_basic_failing.1.rspecish index 09d4e4ba7..0de848b6b 100644 --- a/testdata/matching_basic_failing.1.rspecish +++ b/testdata/out_matching_basic_failing.1.rspecish @@ -1,4 +1,4 @@ -FFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFFFFFFFFF Failures/Skipped: @@ -24,7 +24,7 @@ Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers - [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}}] + [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] Matching: basic_int: matches: Expected @@ -32,6 +32,12 @@ Expected to be == 43 +Matching: basic_len: matches: +Expected + "123" +To satisfy at least one of these matchers + [{"have-len":2}] + Matching: basic_reader: matches: Expected "object: *strings.Reader" @@ -40,18 +46,48 @@ to contain patterns the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] +Matching: basic_semver: matches: +Expected + "1.2.3" +To satisfy at least one of these matchers + [{"semver-constraint":">=9.9.0"}] + Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test" +Matching: basic_string_contain_substring: matches: +Expected + "foo" +to contain substring + "x" + +Matching: basic_string_have_prefix: matches: +Expected + "foo" +to have prefix + "g" + +Matching: basic_string_have_suffix: matches: +Expected + "foo" +to have suffix + "x" + Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" +Matching: negated_and: matches: +Expected + 42 +To not satisfy all of these matchers + [{"":42},{"":42}] + Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] @@ -64,6 +100,12 @@ Expected not to consist of ["foo",{"have-prefix":"m"},"bar"] +Matching: negated_basic_array_contain_element: matches: +Expected + ["foo","bar","moo"] +to not contain element matching + "foo" + Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] @@ -89,6 +131,30 @@ Expected not to equal "this is a test" +Matching: negatedbasic_len: matches: +Expected + "123" +not to have length + 3 + +Matching: negatedbasic_string_contain_substring: matches: +Expected + "foo" +not to contain substring + "oo" + +Matching: negatedbasic_string_have_prefix: matches: +Expected + "foo" +not to have prefix + "f" + +Matching: negatedbasic_string_have_suffix: matches: +Expected + "foo" +not to have suffix + "o" + Matching: negatedbasic_string_regexp: matches: Expected "this is a test" @@ -96,4 +162,4 @@ not to match regular expression "^this" Total Duration: -Count: 14, Failed: 14, Skipped: 0 +Count: 25, Failed: 25, Skipped: 0 diff --git a/testdata/out_matching_basic_failing.1.tap b/testdata/out_matching_basic_failing.1.tap new file mode 100644 index 000000000..4274f5526 --- /dev/null +++ b/testdata/out_matching_basic_failing.1.tap @@ -0,0 +1,26 @@ +1..25 +not ok 1 - Matching: basic_array: matches: Expected ["group1","group2","group3"] to contain elements ["group1","group2","group2","group4"] the missing elements were ["group2","group4"] +not ok 2 - Matching: basic_array_consists_of: matches: Expected ["foo","bar","moo"] to consist of ["fox",{"have-prefix":"t"},"box"] the missing elements were ["fox",{"have-prefix":"t"},"box"] the extra elements were ["foo","bar","moo"] +not ok 3 - Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] +not ok 4 - Matching: basic_int: matches: Expected 42 to be == 43 +not ok 5 - Matching: basic_len: matches: Expected "123" To satisfy at least one of these matchers [{"have-len":2}] +not ok 6 - Matching: basic_reader: matches: Expected "object: *strings.Reader" to contain patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] +not ok 7 - Matching: basic_semver: matches: Expected "1.2.3" To satisfy at least one of these matchers [{"semver-constraint":">=9.9.0"}] +not ok 8 - Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test" +not ok 9 - Matching: basic_string_contain_substring: matches: Expected "foo" to contain substring "x" +not ok 10 - Matching: basic_string_have_prefix: matches: Expected "foo" to have prefix "g" +not ok 11 - Matching: basic_string_have_suffix: matches: Expected "foo" to have suffix "x" +not ok 12 - Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" +not ok 13 - Matching: negated_and: matches: Expected 42 To not satisfy all of these matchers [{"":42},{"":42}] +not ok 14 - Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] not to contain elements ["group1","group2","group3"] +not ok 15 - Matching: negated_basic_array_consists_of: matches: Expected ["foo","bar","moo"] not to consist of ["foo",{"have-prefix":"m"},"bar"] +not ok 16 - Matching: negated_basic_array_contain_element: matches: Expected ["foo","bar","moo"] to not contain element matching "foo" +not ok 17 - Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] +not ok 18 - Matching: negated_basic_int: matches: Expected 42 not to be == 42 +not ok 19 - Matching: negated_basic_reader: matches: Error ContainElements matcher expects an array/slice/map. Got: : foo bar moo cow +not ok 20 - Matching: negated_basic_string: matches: Expected "this is a test" not to equal "this is a test" +not ok 21 - Matching: negatedbasic_len: matches: Expected "123" not to have length 3 +not ok 22 - Matching: negatedbasic_string_contain_substring: matches: Expected "foo" not to contain substring "oo" +not ok 23 - Matching: negatedbasic_string_have_prefix: matches: Expected "foo" not to have prefix "f" +not ok 24 - Matching: negatedbasic_string_have_suffix: matches: Expected "foo" not to have suffix "o" +not ok 25 - Matching: negatedbasic_string_regexp: matches: Expected "this is a test" not to match regular expression "^this" diff --git a/testdata/out_matching_basic_failing.2.nagios b/testdata/out_matching_basic_failing.2.nagios new file mode 100644 index 000000000..aa3ddac51 --- /dev/null +++ b/testdata/out_matching_basic_failing.2.nagios @@ -0,0 +1 @@ +GOSS CRITICAL - Count: 25, Failed: 25, Skipped: 0, Duration: diff --git a/testdata/matching_transformers.0.documentation b/testdata/out_matching_transformers.0.documentation similarity index 90% rename from testdata/matching_transformers.0.documentation rename to testdata/out_matching_transformers.0.documentation index 0bce31365..7ceedf623 100644 --- a/testdata/matching_transformers.0.documentation +++ b/testdata/out_matching_transformers.0.documentation @@ -1,5 +1,6 @@ Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"wtf"}},{"not":{"match-regexp":"^ERROR:"}}]} Matching: test_array: matches: matches expectation: [{"contain-element":{"match-regexp":"4."}},"45",{"and":[{"ge":46},{"le":50}]}] +Matching: test_gjson_have_key_array: matches: matches expectation: {"gjson":{"arr":{"or":[{"contain-elements":[{"have-key":"nested"}]}]}}} Matching: test_gjson_transform: matches: matches expectation: {"gjson":{"@this":{"have-key":"foo"},"count":{"le":25},"foo":{"have-prefix":"b"},"moo":{"and":[{"have-key":"nested"},{"not":{"have-key":"nested2"}}]},"moo.nested":"cow"}} Matching: test_gjson_using_this_and_equal: matches: matches expectation: {"gjson":{"@this":{"equal":{"baz":"bing","foo":"bar"}}}} Matching: test_numeric_string: matches: matches expectation: {"and":["128",{"have-prefix":"1"},{"have-suffix":"8"},{"match-regexp":"\\d{3}"}]} @@ -12,4 +13,4 @@ Matching: test_string_numeric: matches: matches expectation: {"and":[128,128,{"l Total Duration: -Count: 11, Failed: 0, Skipped: 0 +Count: 12, Failed: 0, Skipped: 0 diff --git a/testdata/out_matching_transformers.0.nagios b/testdata/out_matching_transformers.0.nagios new file mode 100644 index 000000000..749fcaed1 --- /dev/null +++ b/testdata/out_matching_transformers.0.nagios @@ -0,0 +1 @@ +GOSS OK - Count: 12, Failed: 0, Skipped: 0, Duration: diff --git a/testdata/out_matching_transformers.0.rspecish b/testdata/out_matching_transformers.0.rspecish new file mode 100644 index 000000000..a5b03e65c --- /dev/null +++ b/testdata/out_matching_transformers.0.rspecish @@ -0,0 +1,4 @@ +............ + +Total Duration: +Count: 12, Failed: 0, Skipped: 0 diff --git a/testdata/out_matching_transformers.0.tap b/testdata/out_matching_transformers.0.tap new file mode 100644 index 000000000..1e94a9b6c --- /dev/null +++ b/testdata/out_matching_transformers.0.tap @@ -0,0 +1,13 @@ +1..12 +ok 1 - Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"wtf"}},{"not":{"match-regexp":"^ERROR:"}}]} +ok 2 - Matching: test_array: matches: matches expectation: [{"contain-element":{"match-regexp":"4."}},"45",{"and":[{"ge":46},{"le":50}]}] +ok 3 - Matching: test_gjson_have_key_array: matches: matches expectation: {"gjson":{"arr":{"or":[{"contain-elements":[{"have-key":"nested"}]}]}}} +ok 4 - Matching: test_gjson_transform: matches: matches expectation: {"gjson":{"@this":{"have-key":"foo"},"count":{"le":25},"foo":{"have-prefix":"b"},"moo":{"and":[{"have-key":"nested"},{"not":{"have-key":"nested2"}}]},"moo.nested":"cow"}} +ok 5 - Matching: test_gjson_using_this_and_equal: matches: matches expectation: {"gjson":{"@this":{"equal":{"baz":"bing","foo":"bar"}}}} +ok 6 - Matching: test_numeric_string: matches: matches expectation: {"and":["128",{"have-prefix":"1"},{"have-suffix":"8"},{"match-regexp":"\\d{3}"}]} +ok 7 - Matching: test_reader_as_single_string: matches: matches expectation: "cool" +ok 8 - Matching: test_reader_using_array: matches: matches expectation: ["foo bar","15","moo cow"] +ok 9 - Matching: test_reader_using_int_matchers: matches: matches expectation: {"and":[{"le":250},{"ge":20}]} +ok 10 - Matching: test_reader_using_string_matchers: matches: matches expectation: {"and":[{"have-len":19},"foo bar\n15\nmoo cow\n",{"have-prefix":"foo"},{"have-suffix":"cow\n"},{"contain-element":{"have-prefix":"moo"}},{"contain-elements":[{"not":"this_doesnt_exist"},{"lt":20},{"have-prefix":"moo"}]}]} +ok 11 - Matching: test_string_float: matches: matches expectation: {"and":[128.3,{"le":129},{"gt":120.2}]} +ok 12 - Matching: test_string_numeric: matches: matches expectation: {"and":[128,128,{"le":128},{"gt":120}]} diff --git a/testdata/matching_transformers_failing.1.documentation b/testdata/out_matching_transformers_failing.1.documentation similarity index 81% rename from testdata/matching_transformers_failing.1.documentation rename to testdata/out_matching_transformers_failing.1.documentation index 7e208f4f2..b6f987fda 100644 --- a/testdata/matching_transformers_failing.1.documentation +++ b/testdata/out_matching_transformers_failing.1.documentation @@ -12,6 +12,23 @@ to contain elements [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +Matching: test_gjson_have_key_array: matches: +Expected + {"arr":[{"nested":"cow"},{"nested2":"moo"}]} +To satisfy at least one of these matchers + [{"have-key":"fail"}] +the transform chain was + [{"gjson":{"Path":"@this"}}] +Matching: test_gjson_invalid: matches: +Error + matchers.Gjson{Path:"@this"}: Invalid json +the transform chain was + [{"gjson":{"Path":"@this"}}] +Matching: test_gjson_not_found: matches: +Error + matchers.Gjson{Path:"foo"}: Path not found: foo +the transform chain was + [{"gjson":{"Path":"foo"}}] Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} @@ -119,6 +136,26 @@ to contain elements the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +Matching: test_gjson_have_key_array: matches: +Expected + {"arr":[{"nested":"cow"},{"nested2":"moo"}]} +To satisfy at least one of these matchers + [{"have-key":"fail"}] +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_gjson_invalid: matches: +Error + matchers.Gjson{Path:"@this"}: Invalid json +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_gjson_not_found: matches: +Error + matchers.Gjson{Path:"foo"}: Path not found: foo +the transform chain was + [{"gjson":{"Path":"foo"}}] + Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} @@ -220,4 +257,4 @@ the transform chain was [{"to-numeric":{}}] Total Duration: -Count: 15, Failed: 15, Skipped: 0 +Count: 18, Failed: 18, Skipped: 0 diff --git a/testdata/matching_transformers_failing.1.rspecish b/testdata/out_matching_transformers_failing.1.rspecish similarity index 80% rename from testdata/matching_transformers_failing.1.rspecish rename to testdata/out_matching_transformers_failing.1.rspecish index e790564db..4a57e10df 100644 --- a/testdata/matching_transformers_failing.1.rspecish +++ b/testdata/out_matching_transformers_failing.1.rspecish @@ -1,4 +1,4 @@ -FFFFFFFFFFFFFFF +FFFFFFFFFFFFFFFFFF Failures/Skipped: @@ -18,6 +18,26 @@ to contain elements the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +Matching: test_gjson_have_key_array: matches: +Expected + {"arr":[{"nested":"cow"},{"nested2":"moo"}]} +To satisfy at least one of these matchers + [{"have-key":"fail"}] +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_gjson_invalid: matches: +Error + matchers.Gjson{Path:"@this"}: Invalid json +the transform chain was + [{"gjson":{"Path":"@this"}}] + +Matching: test_gjson_not_found: matches: +Error + matchers.Gjson{Path:"foo"}: Path not found: foo +the transform chain was + [{"gjson":{"Path":"foo"}}] + Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} @@ -119,4 +139,4 @@ the transform chain was [{"to-numeric":{}}] Total Duration: -Count: 15, Failed: 15, Skipped: 0 +Count: 18, Failed: 18, Skipped: 0 diff --git a/testdata/out_matching_transformers_failing.1.tap b/testdata/out_matching_transformers_failing.1.tap new file mode 100644 index 000000000..a8b05f2d6 --- /dev/null +++ b/testdata/out_matching_transformers_failing.1.tap @@ -0,0 +1,19 @@ +1..18 +not ok 1 - Matching: basic_reader_as_array: matches: Expected ["foo bar","moo cow",""] to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] +not ok 2 - Matching: test_array: matches: Expected ["45","46","47"] to contain elements [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] +not ok 3 - Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} To satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] +not ok 4 - Matching: test_gjson_invalid: matches: Error matchers.Gjson{Path:"@this"}: Invalid json the transform chain was [{"gjson":{"Path":"@this"}}] +not ok 5 - Matching: test_gjson_not_found: matches: Error matchers.Gjson{Path:"foo"}: Path not found: foo the transform chain was [{"gjson":{"Path":"foo"}}] +not ok 6 - Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] +not ok 7 - Matching: test_gjson_transform_nested_count: matches: Expected 15 to be <= 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] +not ok 8 - Matching: test_gjson_transform_nested_prefix: matches: Expected "bar" to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] +not ok 9 - Matching: test_gjson_transform_nested_this: matches: Expected {"count":"15","foo":"bar","moo":{"nested":"cow"}} to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] +not ok 10 - Matching: test_gjson_transform_not_key: matches: Expected {"nested":"cow"} not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] +not ok 11 - Matching: test_gjson_transform_simple: matches: Expected "cow" to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] +not ok 12 - Matching: test_gjson_using_this_and_equal: matches: Expected {"baz":"bing","foo":"bar"} to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] +not ok 13 - Matching: test_numeric_string: matches: Expected "128" to equal "129" the transform chain was [{"to-string":{}}] +not ok 14 - Matching: test_reader_as_single_string: matches: Expected "cool" to equal "not-cool" +not ok 15 - Matching: test_reader_using_int_matchers: matches: Expected 40 to be <= 20 the transform chain was [{"to-numeric":{}}] +not ok 16 - Matching: test_reader_using_string_matchers: matches: Expected "foo bar\n15\nmoo cow\n" to have length 15 +not ok 17 - Matching: test_string_float: matches: Expected 128.3 to be == 129.3 the transform chain was [{"to-numeric":{}}] +not ok 18 - Matching: test_string_numeric: matches: Expected 128 to be == 129 the transform chain was [{"to-numeric":{}}] diff --git a/testdata/out_matching_transformers_failing.2.nagios b/testdata/out_matching_transformers_failing.2.nagios new file mode 100644 index 000000000..3e7a6bbf7 --- /dev/null +++ b/testdata/out_matching_transformers_failing.2.nagios @@ -0,0 +1 @@ +GOSS CRITICAL - Count: 18, Failed: 18, Skipped: 0, Duration: From a83de2b248d7632bc2a1653843ed466407089ded Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 3 Jun 2023 21:12:01 -0700 Subject: [PATCH 26/41] Fixed numeric eq bug --- matchers/be_numerically_matcher.go | 7 ++++++- matchers/matchers.go | 4 +++- outputs/outputs.go | 5 ++--- testdata/out_matching_basic_failing.1.documentation | 4 ++-- testdata/out_matching_basic_failing.1.rspecish | 2 +- testdata/out_matching_basic_failing.1.tap | 2 +- 6 files changed, 15 insertions(+), 9 deletions(-) diff --git a/matchers/be_numerically_matcher.go b/matchers/be_numerically_matcher.go index 053de26a0..7abee536f 100644 --- a/matchers/be_numerically_matcher.go +++ b/matchers/be_numerically_matcher.go @@ -38,7 +38,11 @@ func (m *BeNumericallyMatcher) NegatedFailureResult(actual interface{}) MatcherR func (m *BeNumericallyMatcher) MarshalJSON() ([]byte, error) { j := make(map[string]interface{}) - j[numericSymbolToStr[m.Comparator]] = m.CompareTo[0] + str, ok := numericSymbolToStr[m.Comparator] + if !ok { + return []byte{}, fmt.Errorf("unknown comparator %s", m.Comparator) + } + j[str] = m.CompareTo[0] return json.Marshal(j) } @@ -47,4 +51,5 @@ var numericSymbolToStr = map[string]string{ ">=": "ge", "<": "lt", "<=": "le", + "==": "eq", } diff --git a/matchers/matchers.go b/matchers/matchers.go index aba016e09..93990f564 100644 --- a/matchers/matchers.go +++ b/matchers/matchers.go @@ -1,6 +1,7 @@ package matchers import ( + "encoding/json" "reflect" "unsafe" @@ -13,7 +14,8 @@ type GossMatcher interface { //Match(actual interface{}) (success bool, err error) FailureResult(actual interface{}) MatcherResult NegatedFailureResult(actual interface{}) MatcherResult - //json.Marshaler + // This doesn't seem to make a difference, maybe not needed + json.Marshaler } type MatcherResult struct { diff --git a/outputs/outputs.go b/outputs/outputs.go index 40c2a097e..d26a34059 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -118,17 +118,16 @@ func prettyPrintTestResult(t resource.TestResult, compact bool, includeRaw bool) func prettyPrint(i interface{}, indent bool) string { // JSON doesn't like non-string keys i = dyno.ConvertMapI2MapS(i) - // fixme: error handling buffer := &bytes.Buffer{} encoder := json.NewEncoder(buffer) encoder.SetEscapeHTML(false) var b []byte - // []matchers.GossMatcher doesn't print correctly err := encoder.Encode(i) if err == nil { b = buffer.Bytes() } else { - b = []byte(fmt.Sprint(i)) + // FIXME: Is this the right thing to do? + b = []byte(err.Error()) } b = bytes.TrimRightFunc(b, unicode.IsSpace) if indent { diff --git a/testdata/out_matching_basic_failing.1.documentation b/testdata/out_matching_basic_failing.1.documentation index fb5bb733b..7656e6218 100644 --- a/testdata/out_matching_basic_failing.1.documentation +++ b/testdata/out_matching_basic_failing.1.documentation @@ -70,7 +70,7 @@ Matching: negated_and: matches: Expected 42 To not satisfy all of these matchers - [{"":42},{"":42}] + [{"eq":42},{"eq":42}] Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] @@ -220,7 +220,7 @@ Matching: negated_and: matches: Expected 42 To not satisfy all of these matchers - [{"":42},{"":42}] + [{"eq":42},{"eq":42}] Matching: negated_basic_array: matches: Expected diff --git a/testdata/out_matching_basic_failing.1.rspecish b/testdata/out_matching_basic_failing.1.rspecish index 0de848b6b..0754eb60d 100644 --- a/testdata/out_matching_basic_failing.1.rspecish +++ b/testdata/out_matching_basic_failing.1.rspecish @@ -86,7 +86,7 @@ Matching: negated_and: matches: Expected 42 To not satisfy all of these matchers - [{"":42},{"":42}] + [{"eq":42},{"eq":42}] Matching: negated_basic_array: matches: Expected diff --git a/testdata/out_matching_basic_failing.1.tap b/testdata/out_matching_basic_failing.1.tap index 4274f5526..aa46f1677 100644 --- a/testdata/out_matching_basic_failing.1.tap +++ b/testdata/out_matching_basic_failing.1.tap @@ -11,7 +11,7 @@ not ok 9 - Matching: basic_string_contain_substring: matches: Expected "foo" to not ok 10 - Matching: basic_string_have_prefix: matches: Expected "foo" to have prefix "g" not ok 11 - Matching: basic_string_have_suffix: matches: Expected "foo" to have suffix "x" not ok 12 - Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" -not ok 13 - Matching: negated_and: matches: Expected 42 To not satisfy all of these matchers [{"":42},{"":42}] +not ok 13 - Matching: negated_and: matches: Expected 42 To not satisfy all of these matchers [{"eq":42},{"eq":42}] not ok 14 - Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] not to contain elements ["group1","group2","group3"] not ok 15 - Matching: negated_basic_array_consists_of: matches: Expected ["foo","bar","moo"] not to consist of ["foo",{"have-prefix":"m"},"bar"] not ok 16 - Matching: negated_basic_array_contain_element: matches: Expected ["foo","bar","moo"] to not contain element matching "foo" From 07f40df2a6a83638898fd8c8be7e1f6f15cb253b Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 5 Jun 2023 06:31:15 -0700 Subject: [PATCH 27/41] Make output more consistent --- matchers/and.go | 2 +- matchers/be_numerically_matcher.go | 50 ++++++++++++------- matchers/contain_element_matcher.go | 2 +- matchers/have_patterns.go | 4 +- matchers/or.go | 2 +- matchers/semver_constraint.go | 4 +- resource/gomega.go | 11 +--- ...out_matching_basic_failing.1.documentation | 36 ++++++------- .../out_matching_basic_failing.1.rspecish | 18 +++---- testdata/out_matching_basic_failing.1.tap | 18 +++---- ...ching_transformers_failing.1.documentation | 20 ++++---- ...t_matching_transformers_failing.1.rspecish | 10 ++-- .../out_matching_transformers_failing.1.tap | 10 ++-- 13 files changed, 97 insertions(+), 90 deletions(-) diff --git a/matchers/and.go b/matchers/and.go index 12aad5e3b..502083bd5 100644 --- a/matchers/and.go +++ b/matchers/and.go @@ -35,7 +35,7 @@ func (m *AndMatcher) FailureResult(actual interface{}) MatcherResult { func (m *AndMatcher) NegatedFailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: "To not satisfy all of these matchers", + Message: "not to satisfy all of these matchers", Expected: m.Matchers, } } diff --git a/matchers/be_numerically_matcher.go b/matchers/be_numerically_matcher.go index 7abee536f..74b77552e 100644 --- a/matchers/be_numerically_matcher.go +++ b/matchers/be_numerically_matcher.go @@ -8,22 +8,34 @@ import ( ) type BeNumericallyMatcher struct { - matchers.BeNumericallyMatcher + fakeOmegaMatcher + Comparator string + CompareTo []interface{} + //matchers.BeNumericallyMatcher } func BeNumerically(comparator string, compareTo ...interface{}) GossMatcher { return &BeNumericallyMatcher{ - matchers.BeNumericallyMatcher{ - Comparator: comparator, - CompareTo: compareTo, - }, + Comparator: comparator, + CompareTo: compareTo, } } +func (m *BeNumericallyMatcher) Match(actual interface{}) (success bool, err error) { + comparator, err := strToSymbol(m.Comparator) + if err != nil { + return false, err + } + matcher := &matchers.BeNumericallyMatcher{ + Comparator: comparator, + CompareTo: m.CompareTo, + } + return matcher.Match(actual) +} func (m *BeNumericallyMatcher) FailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: fmt.Sprintf("to be %s", m.Comparator), + Message: fmt.Sprintf("to be numerically %s", m.Comparator), Expected: m.CompareTo[0], } } @@ -31,25 +43,27 @@ func (m *BeNumericallyMatcher) FailureResult(actual interface{}) MatcherResult { func (m *BeNumericallyMatcher) NegatedFailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: fmt.Sprintf("not to be %s", m.Comparator), + Message: fmt.Sprintf("not to be numerically %s", m.Comparator), Expected: m.CompareTo[0], } } func (m *BeNumericallyMatcher) MarshalJSON() ([]byte, error) { j := make(map[string]interface{}) - str, ok := numericSymbolToStr[m.Comparator] - if !ok { - return []byte{}, fmt.Errorf("unknown comparator %s", m.Comparator) - } - j[str] = m.CompareTo[0] + j[m.Comparator] = m.CompareTo[0] return json.Marshal(j) } -var numericSymbolToStr = map[string]string{ - ">": "gt", - ">=": "ge", - "<": "lt", - "<=": "le", - "==": "eq", +func strToSymbol(s string) (string, error) { + comparator, ok := map[string]string{ + "gt": ">", + "ge": ">=", + "lt": "<", + "le": "<=", + "eq": "==", + }[s] + if !ok { + return "", fmt.Errorf("Unknown comparator: %s", s) + } + return comparator, nil } diff --git a/matchers/contain_element_matcher.go b/matchers/contain_element_matcher.go index a0c4dd7ae..30a125e1f 100644 --- a/matchers/contain_element_matcher.go +++ b/matchers/contain_element_matcher.go @@ -29,7 +29,7 @@ func (m *ContainElementMatcher) FailureResult(actual interface{}) MatcherResult func (m *ContainElementMatcher) NegatedFailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: "to not contain element matching", + Message: "not to contain element matching", Expected: m.Element, } } diff --git a/matchers/have_patterns.go b/matchers/have_patterns.go index cd892e966..cff7032f1 100644 --- a/matchers/have_patterns.go +++ b/matchers/have_patterns.go @@ -125,7 +125,7 @@ func (m *HavePatternsMatcher) FailureResult(actual interface{}) MatcherResult { } return MatcherResult{ Actual: a, - Message: "to contain patterns", + Message: "to have patterns", Expected: m.Elements, MissingElements: m.missingElements, } @@ -138,7 +138,7 @@ func (m *HavePatternsMatcher) NegatedFailureResult(actual interface{}) MatcherRe } return MatcherResult{ Actual: a, - Message: "not to contain patterns", + Message: "not to have patterns", Expected: m.Elements, } } diff --git a/matchers/or.go b/matchers/or.go index 90ea73d74..f34dc4e3a 100644 --- a/matchers/or.go +++ b/matchers/or.go @@ -35,7 +35,7 @@ func (m *OrMatcher) Match(actual interface{}) (success bool, err error) { func (m *OrMatcher) FailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: "To satisfy at least one of these matchers", + Message: "to satisfy at least one of these matchers", Expected: m.Matchers, } } diff --git a/matchers/semver_constraint.go b/matchers/semver_constraint.go index acbe0a84c..2718a2933 100644 --- a/matchers/semver_constraint.go +++ b/matchers/semver_constraint.go @@ -44,7 +44,7 @@ func (m *BeSemverConstraintMatcher) Match(actual interface{}) (success bool, err func (m *BeSemverConstraintMatcher) FailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: "to satisfy constraint", + Message: "to satisfy semver constraint", Expected: m.Constraint, } } @@ -52,7 +52,7 @@ func (m *BeSemverConstraintMatcher) FailureResult(actual interface{}) MatcherRes func (m *BeSemverConstraintMatcher) NegatedFailureResult(actual interface{}) MatcherResult { return MatcherResult{ Actual: actual, - Message: "not to satisfy constraint", + Message: "not to satisfy semver constraint", Expected: m.Constraint, } } diff --git a/resource/gomega.go b/resource/gomega.go index 6d7c8453e..299662e83 100644 --- a/resource/gomega.go +++ b/resource/gomega.go @@ -12,7 +12,7 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { case string: return matchers.WithSafeTransform(matchers.ToString{}, matchers.Equal(x)), nil case float64, int: - return matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("==", x)), nil + return matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("eq", x)), nil case bool: return matchers.Equal(x), nil case []interface{}: @@ -134,14 +134,7 @@ func matcherToGomegaMatcher(matcher interface{}) (matchers.GossMatcher, error) { } return matchers.Or(subMatchers...), nil case "gt", "ge", "lt", "le": - // Golang json escapes '>', '<' symbols, so we use 'gt', 'le' instead - comparator := map[string]string{ - "gt": ">", - "ge": ">=", - "lt": "<", - "le": "<=", - }[matchType] - return matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(comparator, value)), nil + return matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(matchType, value)), nil case "semver-constraint": v, isStr := value.(string) diff --git a/testdata/out_matching_basic_failing.1.documentation b/testdata/out_matching_basic_failing.1.documentation index 7656e6218..3e6406f26 100644 --- a/testdata/out_matching_basic_failing.1.documentation +++ b/testdata/out_matching_basic_failing.1.documentation @@ -17,29 +17,29 @@ the extra elements were Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] Matching: basic_int: matches: Expected 42 -to be == +to be numerically eq 43 Matching: basic_len: matches: Expected "123" -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"have-len":2}] Matching: basic_reader: matches: Expected "object: *strings.Reader" -to contain patterns +to have patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] Matching: basic_semver: matches: Expected "1.2.3" -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"semver-constraint":">=9.9.0"}] Matching: basic_string: matches: Expected @@ -69,7 +69,7 @@ to match regular expression Matching: negated_and: matches: Expected 42 -To not satisfy all of these matchers +not to satisfy all of these matchers [{"eq":42},{"eq":42}] Matching: negated_basic_array: matches: Expected @@ -84,17 +84,17 @@ not to consist of Matching: negated_basic_array_contain_element: matches: Expected ["foo","bar","moo"] -to not contain element matching +not to contain element matching "foo" Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] Matching: negated_basic_int: matches: Expected 42 -not to be == +not to be numerically eq 42 Matching: negated_basic_reader: matches: Error @@ -157,25 +157,25 @@ the extra elements were Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] Matching: basic_int: matches: Expected 42 -to be == +to be numerically eq 43 Matching: basic_len: matches: Expected "123" -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"have-len":2}] Matching: basic_reader: matches: Expected "object: *strings.Reader" -to contain patterns +to have patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] @@ -183,7 +183,7 @@ the missing elements were Matching: basic_semver: matches: Expected "1.2.3" -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"semver-constraint":">=9.9.0"}] Matching: basic_string: matches: @@ -219,7 +219,7 @@ to match regular expression Matching: negated_and: matches: Expected 42 -To not satisfy all of these matchers +not to satisfy all of these matchers [{"eq":42},{"eq":42}] Matching: negated_basic_array: matches: @@ -237,19 +237,19 @@ not to consist of Matching: negated_basic_array_contain_element: matches: Expected ["foo","bar","moo"] -to not contain element matching +not to contain element matching "foo" Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] Matching: negated_basic_int: matches: Expected 42 -not to be == +not to be numerically eq 42 Matching: negated_basic_reader: matches: diff --git a/testdata/out_matching_basic_failing.1.rspecish b/testdata/out_matching_basic_failing.1.rspecish index 0754eb60d..8618ec70c 100644 --- a/testdata/out_matching_basic_failing.1.rspecish +++ b/testdata/out_matching_basic_failing.1.rspecish @@ -23,25 +23,25 @@ the extra elements were Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] Matching: basic_int: matches: Expected 42 -to be == +to be numerically eq 43 Matching: basic_len: matches: Expected "123" -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"have-len":2}] Matching: basic_reader: matches: Expected "object: *strings.Reader" -to contain patterns +to have patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] @@ -49,7 +49,7 @@ the missing elements were Matching: basic_semver: matches: Expected "1.2.3" -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"semver-constraint":">=9.9.0"}] Matching: basic_string: matches: @@ -85,7 +85,7 @@ to match regular expression Matching: negated_and: matches: Expected 42 -To not satisfy all of these matchers +not to satisfy all of these matchers [{"eq":42},{"eq":42}] Matching: negated_basic_array: matches: @@ -103,19 +103,19 @@ not to consist of Matching: negated_basic_array_contain_element: matches: Expected ["foo","bar","moo"] -to not contain element matching +not to contain element matching "foo" Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] Matching: negated_basic_int: matches: Expected 42 -not to be == +not to be numerically eq 42 Matching: negated_basic_reader: matches: diff --git a/testdata/out_matching_basic_failing.1.tap b/testdata/out_matching_basic_failing.1.tap index aa46f1677..1e7b141ba 100644 --- a/testdata/out_matching_basic_failing.1.tap +++ b/testdata/out_matching_basic_failing.1.tap @@ -1,22 +1,22 @@ 1..25 not ok 1 - Matching: basic_array: matches: Expected ["group1","group2","group3"] to contain elements ["group1","group2","group2","group4"] the missing elements were ["group2","group4"] not ok 2 - Matching: basic_array_consists_of: matches: Expected ["foo","bar","moo"] to consist of ["fox",{"have-prefix":"t"},"box"] the missing elements were ["fox",{"have-prefix":"t"},"box"] the extra elements were ["foo","bar","moo"] -not ok 3 - Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] -not ok 4 - Matching: basic_int: matches: Expected 42 to be == 43 -not ok 5 - Matching: basic_len: matches: Expected "123" To satisfy at least one of these matchers [{"have-len":2}] -not ok 6 - Matching: basic_reader: matches: Expected "object: *strings.Reader" to contain patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] -not ok 7 - Matching: basic_semver: matches: Expected "1.2.3" To satisfy at least one of these matchers [{"semver-constraint":">=9.9.0"}] +not ok 3 - Matching: basic_array_matchers: matches: Expected ["foo","bar","moo"] to satisfy at least one of these matchers [{"contain-elements":["fox","box"]},{"contain-elements":["fox","bax"]},["fox","bax","mox"],{"consist-of":["fox",{"have-prefix":"t"},"box"]},{"contain-element":{"have-prefix":"x"}},{"contain-element":{"have-suffix":"x"}}] +not ok 4 - Matching: basic_int: matches: Expected 42 to be numerically eq 43 +not ok 5 - Matching: basic_len: matches: Expected "123" to satisfy at least one of these matchers [{"have-len":2}] +not ok 6 - Matching: basic_reader: matches: Expected "object: *strings.Reader" to have patterns ["fox","/^t.*w$/","!foo","!/^foo/"] the missing elements were ["fox","/^t.*w$/","!foo","!/^foo/"] +not ok 7 - Matching: basic_semver: matches: Expected "1.2.3" to satisfy at least one of these matchers [{"semver-constraint":">=9.9.0"}] not ok 8 - Matching: basic_string: matches: Expected "this is a test" to equal "this is a failing test" not ok 9 - Matching: basic_string_contain_substring: matches: Expected "foo" to contain substring "x" not ok 10 - Matching: basic_string_have_prefix: matches: Expected "foo" to have prefix "g" not ok 11 - Matching: basic_string_have_suffix: matches: Expected "foo" to have suffix "x" not ok 12 - Matching: basic_string_regexp: matches: Expected "this is a test" to match regular expression "^foo" -not ok 13 - Matching: negated_and: matches: Expected 42 To not satisfy all of these matchers [{"eq":42},{"eq":42}] +not ok 13 - Matching: negated_and: matches: Expected 42 not to satisfy all of these matchers [{"eq":42},{"eq":42}] not ok 14 - Matching: negated_basic_array: matches: Expected ["group1","group2","group3"] not to contain elements ["group1","group2","group3"] not ok 15 - Matching: negated_basic_array_consists_of: matches: Expected ["foo","bar","moo"] not to consist of ["foo",{"have-prefix":"m"},"bar"] -not ok 16 - Matching: negated_basic_array_contain_element: matches: Expected ["foo","bar","moo"] to not contain element matching "foo" -not ok 17 - Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] To satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] -not ok 18 - Matching: negated_basic_int: matches: Expected 42 not to be == 42 +not ok 16 - Matching: negated_basic_array_contain_element: matches: Expected ["foo","bar","moo"] not to contain element matching "foo" +not ok 17 - Matching: negated_basic_array_matchers: matches: Expected ["foo","bar","moo"] to satisfy at least one of these matchers [{"not":{"contain-elements":["foo","bar"]}},{"not":{"contain-elements":["foo","bar"]}},{"not":["foo","bar","moo"]},{"not":{"consist-of":["foo",{"have-prefix":"m"},"bar"]}},{"not":{"contain-element":{"have-prefix":"b"}}}] +not ok 18 - Matching: negated_basic_int: matches: Expected 42 not to be numerically eq 42 not ok 19 - Matching: negated_basic_reader: matches: Error ContainElements matcher expects an array/slice/map. Got: : foo bar moo cow not ok 20 - Matching: negated_basic_string: matches: Expected "this is a test" not to equal "this is a test" not ok 21 - Matching: negatedbasic_len: matches: Expected "123" not to have length 3 diff --git a/testdata/out_matching_transformers_failing.1.documentation b/testdata/out_matching_transformers_failing.1.documentation index b6f987fda..a70b86637 100644 --- a/testdata/out_matching_transformers_failing.1.documentation +++ b/testdata/out_matching_transformers_failing.1.documentation @@ -15,7 +15,7 @@ the missing elements were Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] @@ -39,7 +39,7 @@ the transform chain was Matching: test_gjson_transform_nested_count: matches: Expected 15 -to be <= +to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] @@ -93,7 +93,7 @@ to equal Matching: test_reader_using_int_matchers: matches: Expected 40 -to be <= +to be numerically le 20 the transform chain was [{"to-numeric":{}}] @@ -105,14 +105,14 @@ to have length Matching: test_string_float: matches: Expected 128.3 -to be == +to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] Matching: test_string_numeric: matches: Expected 128 -to be == +to be numerically eq 129 the transform chain was [{"to-numeric":{}}] @@ -139,7 +139,7 @@ the missing elements were Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] @@ -167,7 +167,7 @@ the transform chain was Matching: test_gjson_transform_nested_count: matches: Expected 15 -to be <= +to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] @@ -229,7 +229,7 @@ to equal Matching: test_reader_using_int_matchers: matches: Expected 40 -to be <= +to be numerically le 20 the transform chain was [{"to-numeric":{}}] @@ -243,7 +243,7 @@ to have length Matching: test_string_float: matches: Expected 128.3 -to be == +to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] @@ -251,7 +251,7 @@ the transform chain was Matching: test_string_numeric: matches: Expected 128 -to be == +to be numerically eq 129 the transform chain was [{"to-numeric":{}}] diff --git a/testdata/out_matching_transformers_failing.1.rspecish b/testdata/out_matching_transformers_failing.1.rspecish index 4a57e10df..9994030f8 100644 --- a/testdata/out_matching_transformers_failing.1.rspecish +++ b/testdata/out_matching_transformers_failing.1.rspecish @@ -21,7 +21,7 @@ the missing elements were Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} -To satisfy at least one of these matchers +to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] @@ -49,7 +49,7 @@ the transform chain was Matching: test_gjson_transform_nested_count: matches: Expected 15 -to be <= +to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] @@ -111,7 +111,7 @@ to equal Matching: test_reader_using_int_matchers: matches: Expected 40 -to be <= +to be numerically le 20 the transform chain was [{"to-numeric":{}}] @@ -125,7 +125,7 @@ to have length Matching: test_string_float: matches: Expected 128.3 -to be == +to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] @@ -133,7 +133,7 @@ the transform chain was Matching: test_string_numeric: matches: Expected 128 -to be == +to be numerically eq 129 the transform chain was [{"to-numeric":{}}] diff --git a/testdata/out_matching_transformers_failing.1.tap b/testdata/out_matching_transformers_failing.1.tap index a8b05f2d6..20f3a5c69 100644 --- a/testdata/out_matching_transformers_failing.1.tap +++ b/testdata/out_matching_transformers_failing.1.tap @@ -1,11 +1,11 @@ 1..18 not ok 1 - Matching: basic_reader_as_array: matches: Expected ["foo bar","moo cow",""] to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] not ok 2 - Matching: test_array: matches: Expected ["45","46","47"] to contain elements [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] -not ok 3 - Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} To satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] +not ok 3 - Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] not ok 4 - Matching: test_gjson_invalid: matches: Error matchers.Gjson{Path:"@this"}: Invalid json the transform chain was [{"gjson":{"Path":"@this"}}] not ok 5 - Matching: test_gjson_not_found: matches: Error matchers.Gjson{Path:"foo"}: Path not found: foo the transform chain was [{"gjson":{"Path":"foo"}}] not ok 6 - Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] -not ok 7 - Matching: test_gjson_transform_nested_count: matches: Expected 15 to be <= 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] +not ok 7 - Matching: test_gjson_transform_nested_count: matches: Expected 15 to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] not ok 8 - Matching: test_gjson_transform_nested_prefix: matches: Expected "bar" to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] not ok 9 - Matching: test_gjson_transform_nested_this: matches: Expected {"count":"15","foo":"bar","moo":{"nested":"cow"}} to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] not ok 10 - Matching: test_gjson_transform_not_key: matches: Expected {"nested":"cow"} not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] @@ -13,7 +13,7 @@ not ok 11 - Matching: test_gjson_transform_simple: matches: Expected "cow" to eq not ok 12 - Matching: test_gjson_using_this_and_equal: matches: Expected {"baz":"bing","foo":"bar"} to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] not ok 13 - Matching: test_numeric_string: matches: Expected "128" to equal "129" the transform chain was [{"to-string":{}}] not ok 14 - Matching: test_reader_as_single_string: matches: Expected "cool" to equal "not-cool" -not ok 15 - Matching: test_reader_using_int_matchers: matches: Expected 40 to be <= 20 the transform chain was [{"to-numeric":{}}] +not ok 15 - Matching: test_reader_using_int_matchers: matches: Expected 40 to be numerically le 20 the transform chain was [{"to-numeric":{}}] not ok 16 - Matching: test_reader_using_string_matchers: matches: Expected "foo bar\n15\nmoo cow\n" to have length 15 -not ok 17 - Matching: test_string_float: matches: Expected 128.3 to be == 129.3 the transform chain was [{"to-numeric":{}}] -not ok 18 - Matching: test_string_numeric: matches: Expected 128 to be == 129 the transform chain was [{"to-numeric":{}}] +not ok 17 - Matching: test_string_float: matches: Expected 128.3 to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] +not ok 18 - Matching: test_string_numeric: matches: Expected 128 to be numerically eq 129 the transform chain was [{"to-numeric":{}}] From 46f455a01f82e82f2c3a70a5716a2aa0b05e6302 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Thu, 15 Jun 2023 14:59:23 -0700 Subject: [PATCH 28/41] Update dependencies --- go.mod | 45 +++++++++++++++++++++++++++++++-------------- go.sum | 2 -- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index fe1097a60..bcac9d003 100644 --- a/go.mod +++ b/go.mod @@ -1,40 +1,57 @@ module github.com/aelsabbahy/goss require ( - github.com/Masterminds/goutils v1.1.0 // indirect - github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible - github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2 github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 github.com/blang/semver v3.5.1+incompatible github.com/cheekybits/genny v1.0.0 - github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.10.0 - github.com/go-ole/go-ole v1.2.4 // indirect - github.com/google/uuid v1.1.2 // indirect - github.com/huandu/xstrings v1.3.2 // indirect github.com/icza/dyno v0.0.0-20200205103839-49cb13720835 - github.com/imdario/mergo v0.3.11 // indirect - github.com/kr/pretty v0.1.0 // indirect github.com/miekg/dns v1.1.35 - github.com/mitchellh/copystructure v1.0.0 // indirect github.com/moby/sys/mountinfo v0.4.0 github.com/oleiade/reflections v1.0.1 github.com/onsi/gomega v1.10.4 github.com/opencontainers/runc v0.1.1 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/pkg/errors v0.9.1 // indirect github.com/shirou/gopsutil v3.20.11+incompatible github.com/stretchr/testify v1.6.1 github.com/thoas/go-funk v0.7.0 github.com/tidwall/gjson v1.6.7 github.com/urfave/cli v1.22.5 + gopkg.in/yaml.v2 v2.4.0 + gotest.tools/v3 v3.0.3 +) + +require ( + github.com/Masterminds/goutils v1.1.0 // indirect + github.com/Masterminds/semver v1.5.0 // indirect + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-ole/go-ole v1.2.4 // indirect + github.com/google/go-cmp v0.4.0 // indirect + github.com/google/uuid v1.1.2 // indirect + github.com/huandu/xstrings v1.3.2 // indirect + github.com/imdario/mergo v0.3.11 // indirect + github.com/kr/pretty v0.1.0 // indirect + github.com/mattn/go-colorable v0.1.8 // indirect + github.com/mattn/go-isatty v0.0.12 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/reflectwalk v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/tidwall/match v1.0.3 // indirect + github.com/tidwall/pretty v1.0.2 // indirect golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect + golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect + golang.org/x/text v0.3.3 // indirect + golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/yaml.v2 v2.4.0 - gotest.tools/v3 v3.0.3 + gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) -go 1.13 +go 1.18 diff --git a/go.sum b/go.sum index 56bb87f21..d52ec93c0 100644 --- a/go.sum +++ b/go.sum @@ -32,7 +32,6 @@ github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:x github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -148,7 +147,6 @@ google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= From 46391750f1d1b6c8082432d60b4f639bd1be4aaf Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Fri, 23 Jun 2023 18:00:40 -0700 Subject: [PATCH 29/41] Fix some merge conflicts --- go.mod | 16 +- go.sum | 284 +++++++++++++++--- goss_test.go | 4 +- integration-tests/Dockerfile_trusty | 3 +- integration-tests/goss/centos7/goss.yaml | 22 +- integration-tests/run-tests-alpha.sh | 29 ++ integration-tests/test.sh | 2 +- matcher_test.go | 5 +- matchers/equal_matcher.go | 3 - outputs/bench.go | 4 +- outputs/json_oneline.go | 59 ---- outputs/prometheus.go | 6 +- outputs/prometheus_test.go | 2 +- outputs/rspecish.go | 2 +- outputs/traces.go | 8 +- release-build.sh | 4 + resource/kernel_param.go | 2 +- resource/validate.go | 6 +- resource/validate_test.go | 6 +- serve.go | 2 +- system/system.go | 2 +- testdata/matching_basic.yaml | 2 +- testdata/matching_basic_failing.yaml | 2 +- testdata/matching_transformers.yaml | 2 +- testdata/out_matching_basic.0.documentation | 2 +- testdata/out_matching_basic.0.tap | 2 +- .../out_matching_transformers.0.documentation | 2 +- testdata/out_matching_transformers.0.tap | 2 +- util/config.go | 5 +- validate.go | 4 - 30 files changed, 341 insertions(+), 153 deletions(-) create mode 100755 integration-tests/run-tests-alpha.sh delete mode 100644 outputs/json_oneline.go diff --git a/go.mod b/go.mod index fbaf17b13..103382e23 100644 --- a/go.mod +++ b/go.mod @@ -9,16 +9,19 @@ require ( github.com/cheekybits/genny v1.0.0 github.com/fatih/color v1.15.0 github.com/goss-org/GOnetstat v0.0.0-20230101144325-22be0bd9e64d - github.com/goss-org/go-ps v0.0.0-20230101144351-953ade48a71b github.com/hashicorp/logutils v1.0.0 + github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 github.com/miekg/dns v1.1.52 github.com/moby/sys/mountinfo v0.6.2 github.com/oleiade/reflections v1.0.1 github.com/onsi/gomega v1.27.4 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/prometheus/client_golang v1.14.0 - github.com/prometheus/common v0.42.0 + github.com/prometheus/client_golang v1.7.1 + github.com/prometheus/common v0.10.0 + github.com/shirou/gopsutil v3.21.11+incompatible github.com/stretchr/testify v1.8.1 + github.com/thoas/go-funk v0.9.3 + github.com/tidwall/gjson v1.14.4 github.com/urfave/cli v1.22.12 gopkg.in/yaml.v2 v2.4.0 gotest.tools/v3 v3.0.3 @@ -31,6 +34,7 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 // indirect @@ -41,12 +45,18 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/pkg/errors v0.8.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect golang.org/x/crypto v0.7.0 // indirect golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.8.0 // indirect diff --git a/go.sum b/go.sum index 648c7866f..c076c5434 100644 --- a/go.sum +++ b/go.sum @@ -1,60 +1,266 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= -github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2 h1:NYoPVh1XuUB5VBWLXRKoqzQhl4bajIxh+XuURbJ0uwc= github.com/achanda/go-sysctl v0.0.0-20160222034550-6be7678c45d2/go.mod h1:DCNKSpXhum14Y258jSbRmJvcesbzEdBPincz7yJUx3k= -github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08 h1:oD15ssIOuFLi64zhkPRsaIDvhx4PeZb2QdQoR/wKY2g= -github.com/aelsabbahy/GOnetstat v0.0.0-20160428114218-edf89f784e08/go.mod h1:FETZSu2VGNDJbGfeRExaz/SNbX0TTaqJEMo1yvsKoZ8= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= -github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= -github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/icza/dyno v0.0.0-20200205103839-49cb13720835 h1:f1irK5f03uGGj+FjgQfZ5VhdKNVQVJ4skHsedzVohQ4= -github.com/icza/dyno v0.0.0-20200205103839-49cb13720835/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk= -github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/goss-org/GOnetstat v0.0.0-20230101144325-22be0bd9e64d h1:50mlZKtg8BUvBtFs0ioVpSgMMwcKaJefg/2pZ+lQf98= +github.com/goss-org/GOnetstat v0.0.0-20230101144325-22be0bd9e64d/go.mod h1:MBdRlloGIbpQVDuH5Gxg3hjqwZBCZsmFqbYPaeR6r0M= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 h1:nHoRIX8iXob3Y2kdt9KsjyIb7iApSvb3vgsd93xb5Ow= +github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0/go.mod h1:c1tRKs5Tx7E2+uHGSyyncziFjvGpgv4H2HrqXeUQ/Uk= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/imdario/mergo v0.3.14 h1:fOqeC1+nCuuk6PKQdg9YmosXX7Y7mHX6R/0ZldI9iHo= +github.com/imdario/mergo v0.3.14/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8= -github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs= -github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= +github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM= +github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60= +github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= +github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= +github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= +github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= +github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= +github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= +github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= +github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= diff --git a/goss_test.go b/goss_test.go index 103f4ecb5..16f23084d 100644 --- a/goss_test.go +++ b/goss_test.go @@ -149,7 +149,7 @@ func TestSkipResourcesByType(t *testing.T) { } } - if skipped != 6 { - t.Fatalf("Expected to skip 6 tests, skipped %d", skipped) + if skipped != 5 { + t.Fatalf("Expected to skip 5 tests, skipped %d", skipped) } } diff --git a/integration-tests/Dockerfile_trusty b/integration-tests/Dockerfile_trusty index 04e0f8235..975b39eb0 100644 --- a/integration-tests/Dockerfile_trusty +++ b/integration-tests/Dockerfile_trusty @@ -4,7 +4,8 @@ MAINTAINER Ahmed RUN apt-get update && \ apt-get install -y apache2=2.4.7-1ubuntu4.22 tinyproxy && \ apt-get remove -y vim-tiny && \ - apt-get clean + apt-get clean && \ + echo manual > /etc/init/apache2.override RUN sed -i '/reload|force-reload)/i status) pidof tinyproxy > /dev/null && echo "tinyproxy is running";;' /etc/init.d/tinyproxy RUN sed -i '/start)/a\ touch /var/log/tinyproxy/tinyproxy.log /var/run/tinyproxy/tinyproxy.pid' /etc/init.d/tinyproxy diff --git a/integration-tests/goss/centos7/goss.yaml b/integration-tests/goss/centos7/goss.yaml index e4a7636e7..818b2c34d 100644 --- a/integration-tests/goss/centos7/goss.yaml +++ b/integration-tests/goss/centos7/goss.yaml @@ -1,13 +1,3 @@ -addr: - tcp://127.0.0.1:80: - local-address: 127.0.0.1 - reachable: true - timeout: 500 -port: - tcp:80: - listening: true - ip: - - 0.0.0.0 service: autofs: enabled: false @@ -19,7 +9,7 @@ user: gid: 48 groups: - apache - home: /usr/share/httpd + home: "/usr/share/httpd" group: apache: exists: true @@ -27,6 +17,16 @@ group: process: httpd: running: true +port: + tcp:80: + listening: true + ip: + - '0.0.0.0' +addr: + tcp://127.0.0.1:80: + reachable: true + timeout: 500 + local-address: 127.0.0.1 gossfile: "../goss-s*.yaml": {} bypath: diff --git a/integration-tests/run-tests-alpha.sh b/integration-tests/run-tests-alpha.sh new file mode 100755 index 000000000..05676f050 --- /dev/null +++ b/integration-tests/run-tests-alpha.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# shellcheck source=../ci/lib/setup.sh +source "$(dirname "${BASH_SOURCE[0]}")/../ci/lib/setup.sh" || exit 67 + +platform_spec="${1:?"Must supply name of release binary to build e.g. goss-linux-amd64"}" +# Split platform_spec into platform/arch segments +IFS='- ' read -r -a segments <<< "${platform_spec}" + +os="${segments[0]}" +arch="${segments[1]}" +if [[ "${segments[0]}" == "alpha" ]]; then + os="${segments[1]}" + arch="${segments[2]}" +fi + +repo_root="$(git rev-parse --show-toplevel)" +export GOSS_BINARY="${repo_root}/release/goss-${platform_spec}" +log_info "Using: '${GOSS_BINARY}', cwd: '$(pwd)', os: ${os}" +readarray -t goss_test_files < <(find integration-tests -type f -name "*.goss.yaml" | grep "${os}" | sort | uniq) + +export GOSS_USE_ALPHA=1 +for file in "${goss_test_files[@]}"; do + args=( + "-g=${file}" + "validate" + ) + log_action -e "\nTesting \`${GOSS_BINARY} ${args[*]}\` ...\n" + "${GOSS_BINARY}" "${args[@]}" +done diff --git a/integration-tests/test.sh b/integration-tests/test.sh index 98cd498b8..ed941cac9 100755 --- a/integration-tests/test.sh +++ b/integration-tests/test.sh @@ -44,7 +44,7 @@ echo "$out" if [[ $os == "arch" ]]; then egrep -q 'Count: 99, Failed: 0, Skipped: 3' <<<"$out" else - egrep -q 'Count: 119, Failed: 0, Skipped: 5' <<<"$out" + egrep -q 'Count: 120, Failed: 0, Skipped: 5' <<<"$out" fi if [[ ! $os == "arch" ]]; then diff --git a/matcher_test.go b/matcher_test.go index 5abd96bbd..76e165e6e 100644 --- a/matcher_test.go +++ b/matcher_test.go @@ -11,7 +11,7 @@ import ( "strings" "testing" - "github.com/aelsabbahy/goss/util" + "github.com/goss-org/goss/util" "github.com/stretchr/testify/assert" ) @@ -39,7 +39,6 @@ func TestMatchers(t *testing.T) { if err != nil { t.Fatal(err) } - //tn := tf + " " + outFormat + wantCode tn := outFile t.Run(tn, func(t *testing.T) { output := &bytes.Buffer{} @@ -57,8 +56,6 @@ func TestMatchers(t *testing.T) { actualOut := output.String() actualOut = sanitizeOutput(actualOut) - //outFile := strings.TrimSuffix(tf, filepath.Ext(tf)) - //outFile = fmt.Sprintf("%s.%d.%s", outFile, exitCode, of) if *update { os.WriteFile(outFile, []byte(actualOut), 0644) } diff --git a/matchers/equal_matcher.go b/matchers/equal_matcher.go index 8fe5ef273..faa2125ea 100644 --- a/matchers/equal_matcher.go +++ b/matchers/equal_matcher.go @@ -36,7 +36,4 @@ func (m *EqualMatcher) NegatedFailureResult(actual interface{}) MatcherResult { func (m *EqualMatcher) MarshalJSON() ([]byte, error) { return json.Marshal(m.Expected) - j := make(map[string]interface{}) - j["equal"] = m.Expected - return json.Marshal(j) } diff --git a/outputs/bench.go b/outputs/bench.go index df2298805..aebf9b181 100644 --- a/outputs/bench.go +++ b/outputs/bench.go @@ -5,8 +5,8 @@ import ( "io" "time" - "github.com/aelsabbahy/goss/resource" - "github.com/aelsabbahy/goss/util" + "github.com/goss-org/goss/resource" + "github.com/goss-org/goss/util" ) type Bench struct{} diff --git a/outputs/json_oneline.go b/outputs/json_oneline.go deleted file mode 100644 index 35dee2267..000000000 --- a/outputs/json_oneline.go +++ /dev/null @@ -1,59 +0,0 @@ -package outputs - -import ( - "encoding/json" - "fmt" - "io" - "time" - - "github.com/fatih/color" - "github.com/goss-org/goss/resource" - "github.com/goss-org/goss/util" -) - -type JsonOneline struct{} - -func (r JsonOneline) ValidOptions() []*formatOption { - return []*formatOption{} -} - -func (r JsonOneline) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { - - color.NoColor = true - testCount := 0 - failed := 0 - var resultsOut []map[string]any - for resultGroup := range results { - for _, testResult := range resultGroup { - if !testResult.Successful { - failed++ - } - m := struct2map(testResult) - m["summary-line"] = humanizeResult(testResult) - m["duration"] = int64(m["duration"].(float64)) - resultsOut = append(resultsOut, m) - testCount++ - } - } - - summary := make(map[string]any) - duration := time.Since(startTime) - summary["test-count"] = testCount - summary["failed-count"] = failed - summary["total-duration"] = duration - summary["summary-line"] = fmt.Sprintf("Count: %d, Failed: %d, Duration: %.3fs", testCount, failed, duration.Seconds()) - - out := make(map[string]any) - out["results"] = resultsOut - out["summary"] = summary - - j, _ := json.Marshal(out) - fmt.Fprintln(w, string(j)) - - if failed > 0 { - return 1 - } - - return 0 -} diff --git a/outputs/prometheus.go b/outputs/prometheus.go index be9c51b9d..21be45ec3 100644 --- a/outputs/prometheus.go +++ b/outputs/prometheus.go @@ -73,10 +73,14 @@ func (r Prometheus) ValidOptions() []*formatOption { // Output converts the results into the prometheus text-format. func (r Prometheus) Output(w io.Writer, results <-chan []resource.TestResult, - startTime time.Time, outConfig util.OutputConfig) (exitCode int) { + outConfig util.OutputConfig) (exitCode int) { overallOutcome := resource.OutcomeUnknown + var startTime time.Time for resultGroup := range results { for _, tr := range resultGroup { + if startTime.IsZero() || tr.StartTime.Before(startTime) { + startTime = tr.StartTime + } resType := strings.ToLower(tr.ResourceType) outcome := tr.ToOutcome() testOutcomes.WithLabelValues(resType, outcome).Inc() diff --git a/outputs/prometheus_test.go b/outputs/prometheus_test.go index 82a785dc7..3a4689c82 100644 --- a/outputs/prometheus_test.go +++ b/outputs/prometheus_test.go @@ -37,7 +37,7 @@ func TestPrometheusOutput(t *testing.T) { }, } - exitCode := outputer.Output(buf, makeResults(injectedResults...), time.Now().Add(-1*time.Minute), util.OutputConfig{}) + exitCode := outputer.Output(buf, makeResults(injectedResults...), util.OutputConfig{}) assert.Equal(t, 0, exitCode) output := buf.String() diff --git a/outputs/rspecish.go b/outputs/rspecish.go index 15f46fdde..d56b6a902 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -64,7 +64,7 @@ func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped, includeRaw)) - outstr := summary(startTime, endtime, testCount, failed, skipped) + outstr := summary(startTime, endTime, testCount, failed, skipped) fmt.Fprint(w, outstr) resstr := strings.ReplaceAll(outstr, "\n", " ") if failed > 0 { diff --git a/outputs/traces.go b/outputs/traces.go index 1f5ceed1b..2a37d5da4 100644 --- a/outputs/traces.go +++ b/outputs/traces.go @@ -14,8 +14,8 @@ func logTrace(level string, msg string, testResult resource.TestResult, withIntR testResult.ResourceType, testResult.ResourceId, testResult.Property, - testResult.Expected, - testResult.Found, + testResult.MatcherResult.Expected, + testResult.MatcherResult.Actual, testResult.Duration.Seconds(), testResult.Result, ) @@ -26,8 +26,8 @@ func logTrace(level string, msg string, testResult resource.TestResult, withIntR testResult.ResourceType, testResult.ResourceId, testResult.Property, - testResult.Expected, - testResult.Found, + testResult.MatcherResult.Expected, + testResult.MatcherResult.Actual, testResult.Duration.Seconds(), ) } diff --git a/release-build.sh b/release-build.sh index 2ebbaaf06..be10380d5 100755 --- a/release-build.sh +++ b/release-build.sh @@ -9,6 +9,10 @@ IFS='- ' read -r -a segments <<< "${platform_spec}" os="${segments[0]}" arch="${segments[1]}" +if [[ "${segments[0]}" == "alpha" ]]; then + os="${segments[1]}" + arch="${segments[2]}" +fi output_dir="release/" output_fname="goss-${platform_spec}" diff --git a/resource/kernel_param.go b/resource/kernel_param.go index edc24f445..a8e3d9f56 100644 --- a/resource/kernel_param.go +++ b/resource/kernel_param.go @@ -49,7 +49,7 @@ func (k *KernelParam) GetName() string { } func (k *KernelParam) Validate(sys *system.System) []TestResult { - skip := a.Skip + skip := k.Skip sysKernelParam := sys.NewKernelParam(k.GetName(), sys, util.Config{}) var results []TestResult diff --git a/resource/validate.go b/resource/validate.go index 5494c72c3..95525eaf3 100644 --- a/resource/validate.go +++ b/resource/validate.go @@ -69,7 +69,7 @@ type TestResult struct { Meta meta `json:"meta" yaml:"meta"` // Result - Result string `json:"result" yaml:"result"` + Result int `json:"result" yaml:"result"` Err *ValidateError `json:"err" yaml:"err"` MatcherResult matchers.MatcherResult `json:"matcher-result" yaml:"matcher-result"` StartTime time.Time `json:"start-time" yaml:"start-time"` @@ -184,7 +184,7 @@ func ValidateGomegaValue(res ResourceRead, property string, expectedValue any, a Title: title, Meta: meta, Property: property, - Err: err, + Err: toValidateError(err), StartTime: startTime, EndTime: endTime, Duration: endTime.Sub(startTime), @@ -215,7 +215,7 @@ func ValidateGomegaValue(res ResourceRead, property string, expectedValue any, a Meta: meta, Property: property, MatcherResult: matcherResult, - Err: err, + Err: toValidateError(err), StartTime: startTime, EndTime: endTime, Duration: endTime.Sub(startTime), diff --git a/resource/validate_test.go b/resource/validate_test.go index 680aca191..55a450205 100644 --- a/resource/validate_test.go +++ b/resource/validate_test.go @@ -21,7 +21,7 @@ func (f *FakeResource) GetMeta() meta { return meta{"foo": "bar"} } var stringTests = []struct { in, in2 any - want string + want int }{ {"", "", SUCCESS}, {"foo", "foo", SUCCESS}, @@ -78,7 +78,7 @@ func BenchmarkValidateValue(b *testing.B) { var containsTests = []struct { in []interface{} in2 string - want string + want int }{ {[]interface{}{""}, "", SUCCESS}, {[]interface{}{"foo"}, "foo\nbar", SUCCESS}, @@ -145,7 +145,7 @@ func TestResultMarshaling(t *testing.T) { inFunc := func() (io.Reader, error) { return nil, fmt.Errorf("dummy error") } - res := ValidateContains(&FakeResource{}, "", []string{"x"}, inFunc, false) + res := ValidateValue(&FakeResource{}, "", []string{"x"}, inFunc, false) if res.Err == nil { t.Fatalf("Expected to receive an error") } diff --git a/serve.go b/serve.go index 74193ab27..3adb34270 100644 --- a/serve.go +++ b/serve.go @@ -131,7 +131,7 @@ func (h healthHandler) output(trc <-chan []resource.TestResult, outputer outputs func (h healthHandler) validate() [][]resource.TestResult { h.sys = system.New(h.c.PackageManager) res := make([][]resource.TestResult, 0) - tr := validate(h.sys, h.gossConfig, h.maxConcurrent) + tr := validate(h.sys, h.gossConfig, h.c.DisabledResourceTypes, h.maxConcurrent) for i := range tr { res = append(res, i) } diff --git a/system/system.go b/system/system.go index 541e597a0..1ca542321 100644 --- a/system/system.go +++ b/system/system.go @@ -7,7 +7,7 @@ import ( "strconv" "sync" - "github.com/aelsabbahy/GOnetstat" + "github.com/goss-org/GOnetstat" "github.com/shirou/gopsutil/process" // This needs a better name diff --git a/testdata/matching_basic.yaml b/testdata/matching_basic.yaml index ebc0e2b34..edd9ee672 100644 --- a/testdata/matching_basic.yaml +++ b/testdata/matching_basic.yaml @@ -53,7 +53,7 @@ matching: matches: - 'foo' - '/^m.*w$/' - - '!wtf' + - '!ftw' - '!/^ERROR:/' # Negated diff --git a/testdata/matching_basic_failing.yaml b/testdata/matching_basic_failing.yaml index b55d13cb3..51a6a4277 100644 --- a/testdata/matching_basic_failing.yaml +++ b/testdata/matching_basic_failing.yaml @@ -177,5 +177,5 @@ matching: not: - 'foo' - '/^m.*w$/' - - '!wtf' + - '!ftw' - '!/^ERROR:/' diff --git a/testdata/matching_transformers.yaml b/testdata/matching_transformers.yaml index 0a10ea39e..e30100714 100644 --- a/testdata/matching_transformers.yaml +++ b/testdata/matching_transformers.yaml @@ -8,7 +8,7 @@ matching: and: - contain-element: {contain-substring: 'foo'} - contain-element: {match-regexp: '^m.*w$'} - - not: {contain-substring: 'wtf'} + - not: {contain-substring: 'ftw'} - not: {match-regexp: '^ERROR:'} test_numeric_string: diff --git a/testdata/out_matching_basic.0.documentation b/testdata/out_matching_basic.0.documentation index 6aca46e33..d96a8e279 100644 --- a/testdata/out_matching_basic.0.documentation +++ b/testdata/out_matching_basic.0.documentation @@ -1,7 +1,7 @@ Matching: basic_array: matches: matches expectation: ["group1","group2"] Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}},{"contain-element":{"have-suffix":"r"}}]} Matching: basic_int: matches: matches expectation: 42 -Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!ftw","!/^ERROR:/"] Matching: basic_semver: matches: matches expectation: {"semver-constraint":">=1.2.0"} Matching: basic_string: matches: matches expectation: "this is a test" Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} diff --git a/testdata/out_matching_basic.0.tap b/testdata/out_matching_basic.0.tap index 9a1b11604..0ab691b7c 100644 --- a/testdata/out_matching_basic.0.tap +++ b/testdata/out_matching_basic.0.tap @@ -2,7 +2,7 @@ ok 1 - Matching: basic_array: matches: matches expectation: ["group1","group2"] ok 2 - Matching: basic_array_matchers: matches: matches expectation: {"and":[{"contain-elements":["foo","bar"]},["foo","bar"],{"equal":["foo","bar","moo"]},{"consist-of":["foo",{"have-prefix":"m"},"bar"]},{"contain-element":{"have-prefix":"b"}},{"contain-element":{"have-suffix":"r"}}]} ok 3 - Matching: basic_int: matches: matches expectation: 42 -ok 4 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!wtf","!/^ERROR:/"] +ok 4 - Matching: basic_reader: matches: matches expectation: ["foo","/^m.*w$/","!ftw","!/^ERROR:/"] ok 5 - Matching: basic_semver: matches: matches expectation: {"semver-constraint":">=1.2.0"} ok 6 - Matching: basic_string: matches: matches expectation: "this is a test" ok 7 - Matching: basic_string_regexp: matches: matches expectation: {"match-regexp":"^this"} diff --git a/testdata/out_matching_transformers.0.documentation b/testdata/out_matching_transformers.0.documentation index 7ceedf623..136a6449a 100644 --- a/testdata/out_matching_transformers.0.documentation +++ b/testdata/out_matching_transformers.0.documentation @@ -1,4 +1,4 @@ -Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"wtf"}},{"not":{"match-regexp":"^ERROR:"}}]} +Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"ftw"}},{"not":{"match-regexp":"^ERROR:"}}]} Matching: test_array: matches: matches expectation: [{"contain-element":{"match-regexp":"4."}},"45",{"and":[{"ge":46},{"le":50}]}] Matching: test_gjson_have_key_array: matches: matches expectation: {"gjson":{"arr":{"or":[{"contain-elements":[{"have-key":"nested"}]}]}}} Matching: test_gjson_transform: matches: matches expectation: {"gjson":{"@this":{"have-key":"foo"},"count":{"le":25},"foo":{"have-prefix":"b"},"moo":{"and":[{"have-key":"nested"},{"not":{"have-key":"nested2"}}]},"moo.nested":"cow"}} diff --git a/testdata/out_matching_transformers.0.tap b/testdata/out_matching_transformers.0.tap index 1e94a9b6c..e01c41d1d 100644 --- a/testdata/out_matching_transformers.0.tap +++ b/testdata/out_matching_transformers.0.tap @@ -1,5 +1,5 @@ 1..12 -ok 1 - Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"wtf"}},{"not":{"match-regexp":"^ERROR:"}}]} +ok 1 - Matching: basic_reader_as_array: matches: matches expectation: {"and":[{"contain-element":{"contain-substring":"foo"}},{"contain-element":{"match-regexp":"^m.*w$"}},{"not":{"contain-substring":"ftw"}},{"not":{"match-regexp":"^ERROR:"}}]} ok 2 - Matching: test_array: matches: matches expectation: [{"contain-element":{"match-regexp":"4."}},"45",{"and":[{"ge":46},{"le":50}]}] ok 3 - Matching: test_gjson_have_key_array: matches: matches expectation: {"gjson":{"arr":{"or":[{"contain-elements":[{"have-key":"nested"}]}]}}} ok 4 - Matching: test_gjson_transform: matches: matches expectation: {"gjson":{"@this":{"have-key":"foo"},"count":{"le":25},"foo":{"have-prefix":"b"},"moo":{"and":[{"have-key":"nested"},{"not":{"have-key":"nested2"}}]},"moo.nested":"cow"}} diff --git a/util/config.go b/util/config.go index c5a570d4a..81751aed7 100644 --- a/util/config.go +++ b/util/config.go @@ -28,7 +28,6 @@ type Config struct { Endpoint string FormatOptions []string IgnoreList []string - DisabledResourceTypes []string ListenAddress string LocalAddress string LogLevel string @@ -50,8 +49,12 @@ type Config struct { Spec string Timeout time.Duration Username string + CAFile string + CertFile string + KeyFile string Vars string VarsInline string + DisabledResourceTypes []string } // TimeOutMilliSeconds is the timeout as milliseconds diff --git a/validate.go b/validate.go index 38959245d..9e4c394cc 100644 --- a/validate.go +++ b/validate.go @@ -104,10 +104,6 @@ func Validate(c *util.Config) (code int, err error) { if err != nil { return 1, err } - outputConfig := util.OutputConfig{ - FormatOptions: c.FormatOptions, - } - gossConfig, err := getGossConfig(c.Vars, c.VarsInline, c.Spec) if err != nil { return 78, err From 5815f535158ec06c19b348dd0472d398ea8d8bf7 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 24 Jun 2023 20:18:58 -0700 Subject: [PATCH 30/41] Revert "Migrate from go-ps to gopsutil for better process detection (#597)" This reverts commit 85ce2c8d18db26ef01547dcfc17aec2766f1bb21. --- docs/manual.md | 2 ++ go.mod | 6 +----- go.sum | 13 ++----------- system/process.go | 16 +++++++--------- system/system.go | 8 ++++---- 5 files changed, 16 insertions(+), 29 deletions(-) diff --git a/docs/manual.md b/docs/manual.md index e7f11e61a..5ba5b1a7d 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -798,6 +798,8 @@ process: skip: false ``` +**NOTE:** This check is inspecting the name of the binary, not the name of the process. For example, a process with the name `nginx: master process /usr/sbin/nginx` would be checked with the process `nginx`. To discover the binary of a pid run `ps -p -o comm`. + ### service Validates the state of a service. diff --git a/go.mod b/go.mod index 103382e23..34055d825 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/cheekybits/genny v1.0.0 github.com/fatih/color v1.15.0 github.com/goss-org/GOnetstat v0.0.0-20230101144325-22be0bd9e64d + github.com/goss-org/go-ps v0.0.0-20230609005227-7b318e6a56e5 github.com/hashicorp/logutils v1.0.0 github.com/icza/dyno v0.0.0-20230330125955-09f820a8d9c0 github.com/miekg/dns v1.1.52 @@ -18,7 +19,6 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/prometheus/client_golang v1.7.1 github.com/prometheus/common v0.10.0 - github.com/shirou/gopsutil v3.21.11+incompatible github.com/stretchr/testify v1.8.1 github.com/thoas/go-funk v0.9.3 github.com/tidwall/gjson v1.14.4 @@ -34,7 +34,6 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/uuid v1.3.0 // indirect @@ -54,9 +53,6 @@ require ( github.com/spf13/cast v1.5.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect - github.com/tklauser/go-sysconf v0.3.11 // indirect - github.com/tklauser/numcpus v0.6.0 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect golang.org/x/crypto v0.7.0 // indirect golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.8.0 // indirect diff --git a/go.sum b/go.sum index c076c5434..be4e5765b 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,6 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -66,6 +64,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/goss-org/GOnetstat v0.0.0-20230101144325-22be0bd9e64d h1:50mlZKtg8BUvBtFs0ioVpSgMMwcKaJefg/2pZ+lQf98= github.com/goss-org/GOnetstat v0.0.0-20230101144325-22be0bd9e64d/go.mod h1:MBdRlloGIbpQVDuH5Gxg3hjqwZBCZsmFqbYPaeR6r0M= +github.com/goss-org/go-ps v0.0.0-20230609005227-7b318e6a56e5 h1:NW0Jo4leMIrQxNOyOkBu4yBnygI37m0Ey0EUUgvzr+8= +github.com/goss-org/go-ps v0.0.0-20230609005227-7b318e6a56e5/go.mod h1:FYj70SLmogHdTTDGnIVaaK0iczROlsxmoMCwfAUuIE8= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -141,8 +141,6 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= -github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -173,15 +171,9 @@ github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= -github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= -github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= -github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= @@ -210,7 +202,6 @@ golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/system/process.go b/system/process.go index cd7812238..330829b40 100644 --- a/system/process.go +++ b/system/process.go @@ -1,8 +1,8 @@ package system import ( + "github.com/goss-org/go-ps" "github.com/goss-org/goss/util" - "github.com/shirou/gopsutil/process" ) type Process interface { @@ -14,7 +14,7 @@ type Process interface { type DefProcess struct { executable string - procMap map[string][]*process.Process + procMap map[string][]ps.Process err error } @@ -39,7 +39,7 @@ func (p *DefProcess) Pids() ([]int, error) { return pids, p.err } for _, proc := range p.procMap[p.executable] { - pids = append(pids, int(proc.Pid)) + pids = append(pids, proc.Pid()) } return pids, nil } @@ -54,16 +54,14 @@ func (p *DefProcess) Running() (bool, error) { return false, nil } -func GetProcs() (map[string][]*process.Process, error) { - pmap := make(map[string][]*process.Process) - processes, err := process.Processes() +func GetProcs() (map[string][]ps.Process, error) { + pmap := make(map[string][]ps.Process) + processes, err := ps.Processes() if err != nil { return pmap, err } for _, p := range processes { - if pExe, err := p.Name(); err == nil { - pmap[pExe] = append(pmap[pExe], p) - } + pmap[p.Executable()] = append(pmap[p.Executable()], p) } return pmap, nil diff --git a/system/system.go b/system/system.go index 1ca542321..dfef39859 100644 --- a/system/system.go +++ b/system/system.go @@ -8,9 +8,9 @@ import ( "sync" "github.com/goss-org/GOnetstat" - "github.com/shirou/gopsutil/process" - // This needs a better name + "github.com/goss-org/go-ps" + util2 "github.com/goss-org/goss/util" ) @@ -36,7 +36,7 @@ type System struct { NewHTTP func(string, *System, util2.Config) HTTP ports map[string][]GOnetstat.Process portsOnce sync.Once - procMap map[string][]*process.Process + procMap map[string][]ps.Process procOnce sync.Once } @@ -47,7 +47,7 @@ func (s *System) Ports() map[string][]GOnetstat.Process { return s.ports } -func (s *System) ProcMap() (map[string][]*process.Process, error) { +func (s *System) ProcMap() (map[string][]ps.Process, error) { var err error s.procOnce.Do(func() { From 28c1e539e6ba921305d35623eb3c02d366b81df1 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 24 Jun 2023 20:25:47 -0700 Subject: [PATCH 31/41] Revert 1fe5571b6a47735e15ed313f38618c32bd3dc919 change http.headers back to io.Reader --- integration-tests/goss/darwin/tests/http.goss.yaml | 2 +- integration-tests/goss/goss-shared.yaml | 4 ++-- integration-tests/goss/windows/tests/http.goss.yaml | 2 +- system/http.go | 9 +++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/integration-tests/goss/darwin/tests/http.goss.yaml b/integration-tests/goss/darwin/tests/http.goss.yaml index 74191fe35..3ba7b60e9 100644 --- a/integration-tests/goss/darwin/tests/http.goss.yaml +++ b/integration-tests/goss/darwin/tests/http.goss.yaml @@ -8,7 +8,7 @@ http: request-headers: - "Content-Type: text/html" headers: - - "content-type: text/html" + - "Content-Type: text/html" body: - "google" username: "" diff --git a/integration-tests/goss/goss-shared.yaml b/integration-tests/goss/goss-shared.yaml index e0ba83618..c3db8c764 100644 --- a/integration-tests/goss/goss-shared.yaml +++ b/integration-tests/goss/goss-shared.yaml @@ -199,7 +199,7 @@ http: timeout: 5000 request-headers: - "Foo: bar" - headers: ["content-type: application/json"] + headers: ["Content-Type: application/json"] body: ['"Foo": "bar"'] https://httpbin.org/headers?host: status: 200 @@ -210,7 +210,7 @@ http: # need to see if there's a good way around this, maybe local httpbin? #- "Host: example.com" - "Host: httpbin.org" - headers: ["content-type: application/json"] + headers: ["Content-Type: application/json"] # body: ['"Host": "example.com"'] body: ['"Host": "httpbin.org"'] https://httpbin.org/basic-auth/username/secret: diff --git a/integration-tests/goss/windows/tests/http.goss.yaml b/integration-tests/goss/windows/tests/http.goss.yaml index 74191fe35..3ba7b60e9 100644 --- a/integration-tests/goss/windows/tests/http.goss.yaml +++ b/integration-tests/goss/windows/tests/http.goss.yaml @@ -8,7 +8,7 @@ http: request-headers: - "Content-Type: text/html" headers: - - "content-type: text/html" + - "Content-Type: text/html" body: - "google" username: "" diff --git a/system/http.go b/system/http.go index 76106885f..51c669dde 100644 --- a/system/http.go +++ b/system/http.go @@ -18,7 +18,7 @@ import ( type HTTP interface { HTTP() string Status() (int, error) - Headers() ([]string, error) + Headers() (io.Reader, error) Body() (io.Reader, error) Exists() (bool, error) SetAllowInsecure(bool) @@ -70,7 +70,7 @@ func NewDefHTTP(httpStr string, system *System, config util.Config) HTTP { func HeaderToArray(header http.Header) (res []string) { for name, values := range header { for _, value := range values { - res = append(res, strings.ToLower(fmt.Sprintf("%s: %s", name, value))) + res = append(res, fmt.Sprintf("%s: %s", name, value)) } } sort.Strings(res) @@ -186,12 +186,13 @@ func (u *DefHTTP) Status() (int, error) { return u.resp.StatusCode, nil } -func (u *DefHTTP) Headers() ([]string, error) { +func (u *DefHTTP) Headers() (io.Reader, error) { if err := u.setup(); err != nil { return nil, err } - return HeaderToArray(u.resp.Header), nil + var headerString = strings.Join(HeaderToArray(u.resp.Header), "\n") + return strings.NewReader(headerString), nil } func (u *DefHTTP) Body() (io.Reader, error) { From b50a29668afa4cc960842de7c2a91fe436cc96ae Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 24 Jun 2023 21:10:00 -0700 Subject: [PATCH 32/41] FIX: use filepath.Join() in order to be OS agnostic --- matcher_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/matcher_test.go b/matcher_test.go index 76e165e6e..f5cb37265 100644 --- a/matcher_test.go +++ b/matcher_test.go @@ -25,7 +25,7 @@ func TestMain(m *testing.M) { } func TestMatchers(t *testing.T) { - files, err := filepath.Glob("testdata/out_matching_*") + files, err := filepath.Glob(filepath.Join("testdata", "out_matching_*")) if err != nil { t.Fatal(err) } @@ -33,7 +33,8 @@ func TestMatchers(t *testing.T) { for _, outFile := range files { outFile := outFile parts := strings.Split(outFile, ".") - tf := fmt.Sprintf("testdata/%s.yaml", strings.TrimPrefix(parts[0], "testdata/out_")) + tfName := fmt.Sprintf("%s.yaml", strings.TrimPrefix(parts[0], "testdata/out_")) + tf := filepath.Join("testdata", tfName) outFormat := parts[2] wantCode, err := strconv.Atoi(parts[1]) if err != nil { From 9e7b3db0bdac514f791753c1433b1c7934cabeea Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 24 Jun 2023 22:22:37 -0700 Subject: [PATCH 33/41] FIX: only run matcher_tests on linux --- matcher_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/matcher_test.go b/matcher_test.go index f5cb37265..d79425c35 100644 --- a/matcher_test.go +++ b/matcher_test.go @@ -1,3 +1,5 @@ +//go:build linux + package goss import ( From ef7c93323ecd54dea7f55be07f179ab845740b7e Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sun, 25 Jun 2023 11:41:27 -0700 Subject: [PATCH 34/41] FIX: replace failing www.microsoft.com test --- integration-tests/goss/alpine3/goss-expected-q.yaml | 6 +++--- integration-tests/goss/alpine3/goss-expected.yaml | 6 +++--- integration-tests/goss/centos7/goss-expected-q.yaml | 6 +++--- integration-tests/goss/centos7/goss-expected.yaml | 6 +++--- integration-tests/goss/generate_goss.sh | 2 +- integration-tests/goss/trusty/goss-expected-q.yaml | 6 +++--- integration-tests/goss/trusty/goss-expected.yaml | 6 +++--- integration-tests/goss/wheezy/goss-expected-q.yaml | 6 +++--- integration-tests/goss/wheezy/goss-expected.yaml | 6 +++--- 9 files changed, 25 insertions(+), 25 deletions(-) diff --git a/integration-tests/goss/alpine3/goss-expected-q.yaml b/integration-tests/goss/alpine3/goss-expected-q.yaml index 703fdd573..e6f17cf03 100644 --- a/integration-tests/goss/alpine3/goss-expected-q.yaml +++ b/integration-tests/goss/alpine3/goss-expected-q.yaml @@ -111,16 +111,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 diff --git a/integration-tests/goss/alpine3/goss-expected.yaml b/integration-tests/goss/alpine3/goss-expected.yaml index 6a0fbb6f5..27da2b829 100644 --- a/integration-tests/goss/alpine3/goss-expected.yaml +++ b/integration-tests/goss/alpine3/goss-expected.yaml @@ -153,16 +153,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 diff --git a/integration-tests/goss/centos7/goss-expected-q.yaml b/integration-tests/goss/centos7/goss-expected-q.yaml index 46698a33a..349eda8f9 100644 --- a/integration-tests/goss/centos7/goss-expected-q.yaml +++ b/integration-tests/goss/centos7/goss-expected-q.yaml @@ -111,16 +111,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 diff --git a/integration-tests/goss/centos7/goss-expected.yaml b/integration-tests/goss/centos7/goss-expected.yaml index a8404712c..8db6d4646 100644 --- a/integration-tests/goss/centos7/goss-expected.yaml +++ b/integration-tests/goss/centos7/goss-expected.yaml @@ -159,16 +159,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 diff --git a/integration-tests/goss/generate_goss.sh b/integration-tests/goss/generate_goss.sh index 45b4fa8eb..93e822fb6 100755 --- a/integration-tests/goss/generate_goss.sh +++ b/integration-tests/goss/generate_goss.sh @@ -66,7 +66,7 @@ sed -i '/- mode=/d' $SCRIPT_DIR/${OS}/goss-generated-$ARCH.yaml goss a "${args[@]}" http https://www.google.com -goss a "${args[@]}" http https://www.microsoft.com -x http://127.0.0.1:8888 +goss a "${args[@]}" http https://www.apple.com -x http://127.0.0.1:8888 goss a "${args[@]}" http http://google.com -r diff --git a/integration-tests/goss/trusty/goss-expected-q.yaml b/integration-tests/goss/trusty/goss-expected-q.yaml index 3f166ce92..9554f8e82 100644 --- a/integration-tests/goss/trusty/goss-expected-q.yaml +++ b/integration-tests/goss/trusty/goss-expected-q.yaml @@ -111,16 +111,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 diff --git a/integration-tests/goss/trusty/goss-expected.yaml b/integration-tests/goss/trusty/goss-expected.yaml index 34620a557..94e590364 100644 --- a/integration-tests/goss/trusty/goss-expected.yaml +++ b/integration-tests/goss/trusty/goss-expected.yaml @@ -159,16 +159,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 diff --git a/integration-tests/goss/wheezy/goss-expected-q.yaml b/integration-tests/goss/wheezy/goss-expected-q.yaml index 3f166ce92..9554f8e82 100644 --- a/integration-tests/goss/wheezy/goss-expected-q.yaml +++ b/integration-tests/goss/wheezy/goss-expected-q.yaml @@ -111,16 +111,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 diff --git a/integration-tests/goss/wheezy/goss-expected.yaml b/integration-tests/goss/wheezy/goss-expected.yaml index 37d886cd1..27295fbe9 100644 --- a/integration-tests/goss/wheezy/goss-expected.yaml +++ b/integration-tests/goss/wheezy/goss-expected.yaml @@ -159,16 +159,16 @@ http: no-follow-redirects: true timeout: 5000 body: [] - https://www.google.com: + https://www.apple.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - https://www.microsoft.com: + proxy: http://127.0.0.1:8888 + https://www.google.com: status: 200 allow-insecure: false no-follow-redirects: false timeout: 5000 body: [] - proxy: http://127.0.0.1:8888 From d8c9c930433a88550a2a751a92267fa2f9422992 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sun, 25 Jun 2023 12:59:43 -0700 Subject: [PATCH 35/41] FIX: trusty dockerfile merge regression --- integration-tests/Dockerfile_trusty | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/integration-tests/Dockerfile_trusty b/integration-tests/Dockerfile_trusty index 975b39eb0..04e0f8235 100644 --- a/integration-tests/Dockerfile_trusty +++ b/integration-tests/Dockerfile_trusty @@ -4,8 +4,7 @@ MAINTAINER Ahmed RUN apt-get update && \ apt-get install -y apache2=2.4.7-1ubuntu4.22 tinyproxy && \ apt-get remove -y vim-tiny && \ - apt-get clean && \ - echo manual > /etc/init/apache2.override + apt-get clean RUN sed -i '/reload|force-reload)/i status) pidof tinyproxy > /dev/null && echo "tinyproxy is running";;' /etc/init.d/tinyproxy RUN sed -i '/start)/a\ touch /var/log/tinyproxy/tinyproxy.log /var/run/tinyproxy/tinyproxy.pid' /etc/init.d/tinyproxy From 2a48c91bd256a90261c79a12a01ee4e535ee1cf8 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sun, 25 Jun 2023 14:29:01 -0700 Subject: [PATCH 36/41] Run all tests (except failing prometheus) as part of CI. Seems they were not running --- Makefile | 2 +- ci/go-test.sh | 2 +- docs/manual.md | 2 +- matchers/semver_constraint_test.go | 4 ++-- outputs/outputs_test.go | 1 - resource/gomega_test.go | 10 +++++----- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 052a2f2e0..786c7b885 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ install: release/goss-linux-amd64 test: $(info INFO: Starting build $@) - ./ci/go-test.sh $(pkgs) + ./ci/go-test.sh cov: go test -coverpkg=./... -coverprofile=c.out ./... diff --git a/ci/go-test.sh b/ci/go-test.sh index e715f991e..d0623f91f 100755 --- a/ci/go-test.sh +++ b/ci/go-test.sh @@ -3,7 +3,7 @@ set -euo pipefail command -v go -go test -coverprofile="c.out" "${1}" +go test -coverpkg=./... ./... -skip '^TestPrometheus' -coverprofile="c.out" sed 's|github.com/goss-org/goss/||' <"c.out" >"c.out.tmp" diff --git a/docs/manual.md b/docs/manual.md index 5ba5b1a7d..8b201c635 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -798,7 +798,7 @@ process: skip: false ``` -**NOTE:** This check is inspecting the name of the binary, not the name of the process. For example, a process with the name `nginx: master process /usr/sbin/nginx` would be checked with the process `nginx`. To discover the binary of a pid run `ps -p -o comm`. +**NOTE:** This check is inspecting the name of the binary, not the name of the process. For example, a process with the name `nginx: master process /usr/sbin/nginx` would be checked with the process `nginx`. To discover the binary of a pid run `cat -E /proc//comm`. ### service Validates the state of a service. diff --git a/matchers/semver_constraint_test.go b/matchers/semver_constraint_test.go index 1cc47e474..db5e8e1d3 100644 --- a/matchers/semver_constraint_test.go +++ b/matchers/semver_constraint_test.go @@ -52,7 +52,7 @@ func TestBeSemverConstraintMatcher_FailureMessage(t *testing.T) { args: args{actual: "1.0.0"}, wantResult: MatcherResult{ Actual: "1.0.0", - Message: "to satisfy constraint", + Message: "to satisfy semver constraint", Expected: "> 1.1.0", }, }, @@ -179,7 +179,7 @@ func TestBeSemverConstraintMatcher_NegatedFailureMessage(t *testing.T) { args: args{actual: "1.0.0"}, wantResult: MatcherResult{ Actual: "1.0.0", - Message: "not to satisfy constraint", + Message: "not to satisfy semver constraint", Expected: "> 1.1.0", }, }, diff --git a/outputs/outputs_test.go b/outputs/outputs_test.go index e25a29bfb..141722ed5 100644 --- a/outputs/outputs_test.go +++ b/outputs/outputs_test.go @@ -47,7 +47,6 @@ func TestOutputFormatOptions(t *testing.T) { func TestOptionsRegistration(t *testing.T) { registeredOutputs := Outputers() assert.Contains(t, registeredOutputs, "documentation") - assert.Contains(t, registeredOutputs, "json_oneline") assert.Contains(t, registeredOutputs, "json") assert.Contains(t, registeredOutputs, "junit") assert.Contains(t, registeredOutputs, "nagios") diff --git a/resource/gomega_test.go b/resource/gomega_test.go index 040e310c9..bccb3a41c 100644 --- a/resource/gomega_test.go +++ b/resource/gomega_test.go @@ -20,7 +20,7 @@ var gomegaTests = []struct { }, { in: `1`, - want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("==", float64(1))), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("eq", float64(1))), }, { in: `true`, @@ -39,19 +39,19 @@ var gomegaTests = []struct { // Golang json escapes '>', '<' symbols, so we use 'gt', 'le' instead { in: `{"gt": 1}`, - want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(">", float64(1))), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("gt", float64(1))), }, { in: `{"ge": 1}`, - want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically(">=", float64(1))), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("ge", float64(1))), }, { in: `{"lt": 1}`, - want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("<", float64(1))), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("lt", float64(1))), }, { in: `{"le": 1}`, - want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("<=", float64(1))), + want: matchers.WithSafeTransform(matchers.ToNumeric{}, matchers.BeNumerically("le", float64(1))), }, // String From e7e7841ec0c0e31c24d033a1c8e5895b655a53cf Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Sat, 15 Jul 2023 09:53:21 -0700 Subject: [PATCH 37/41] Remove bench output, add some comments --- Makefile | 4 +++ matcher_test.go | 8 +++-- matchers/be_numerically_matcher.go | 1 - outputs/bench.go | 53 ------------------------------ outputs/outputs.go | 1 - outputs/rspecish.go | 3 ++ 6 files changed, 12 insertions(+), 58 deletions(-) delete mode 100644 outputs/bench.go diff --git a/Makefile b/Makefile index 786c7b885..5ddfb3d5e 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,10 @@ push-images: $(info INFO: Starting build $@) development/push_images.sh +# Update the matcher test golden files +update-matcher-tests: + go test -v -run '^TestMatchers' . -update + test-darwin-all: test-short-all test-int-darwin-all # linux _does_ have the docker-style testing, but does _not_ currently have the same style integration tests darwin+windows do, _because_ of the docker-style testing. test-linux-all: test-short-all test-int-64 test-int-32 diff --git a/matcher_test.go b/matcher_test.go index d79425c35..64703307f 100644 --- a/matcher_test.go +++ b/matcher_test.go @@ -18,6 +18,8 @@ import ( ) var ( + // This will generate the "golden files" prior to running the tests. + // helpful when the output is changed and a user doesn't want to update every single expectation file by hand update = flag.Bool("update", false, "update the golden files of this test") ) @@ -35,8 +37,8 @@ func TestMatchers(t *testing.T) { for _, outFile := range files { outFile := outFile parts := strings.Split(outFile, ".") - tfName := fmt.Sprintf("%s.yaml", strings.TrimPrefix(parts[0], "testdata/out_")) - tf := filepath.Join("testdata", tfName) + specName := fmt.Sprintf("%s.yaml", strings.TrimPrefix(parts[0], "testdata/out_")) + specFile := filepath.Join("testdata", specName) outFormat := parts[2] wantCode, err := strconv.Atoi(parts[1]) if err != nil { @@ -49,7 +51,7 @@ func TestMatchers(t *testing.T) { cfg, err := util.NewConfig( util.WithOutputFormat(outFormat), util.WithResultWriter(output), - util.WithSpecFile(tf), + util.WithSpecFile(specFile), util.WithFormatOptions("sort", "pretty"), ) if err != nil { diff --git a/matchers/be_numerically_matcher.go b/matchers/be_numerically_matcher.go index 74b77552e..ca23030ad 100644 --- a/matchers/be_numerically_matcher.go +++ b/matchers/be_numerically_matcher.go @@ -11,7 +11,6 @@ type BeNumericallyMatcher struct { fakeOmegaMatcher Comparator string CompareTo []interface{} - //matchers.BeNumericallyMatcher } func BeNumerically(comparator string, compareTo ...interface{}) GossMatcher { diff --git a/outputs/bench.go b/outputs/bench.go deleted file mode 100644 index aebf9b181..000000000 --- a/outputs/bench.go +++ /dev/null @@ -1,53 +0,0 @@ -package outputs - -import ( - "fmt" - "io" - "time" - - "github.com/goss-org/goss/resource" - "github.com/goss-org/goss/util" -) - -type Bench struct{} - -func (r Bench) ValidOptions() []*formatOption { - return []*formatOption{ - {name: foSort}, - } -} - -func (r Bench) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) - - sort := util.IsValueInList(foSort, outConfig.FormatOptions) - results = getResults(results, sort) - - var startTime time.Time - var endTime time.Time - var testCount, skipped, failed int - for resultGroup := range results { - for _, testResult := range resultGroup { - if startTime.IsZero() || testResult.StartTime.Before(startTime) { - startTime = testResult.StartTime - } - if endTime.IsZero() || testResult.EndTime.After(endTime) { - endTime = testResult.EndTime - } - fmt.Fprintf(w, "%v %s\n", testResult.Duration, humanizeResult(testResult, true, includeRaw)) - switch testResult.Result { - case resource.SKIP: - skipped++ - case resource.FAIL: - failed++ - } - testCount++ - } - } - - fmt.Fprint(w, summary(startTime, endTime, testCount, failed, skipped)) - if failed > 0 { - return 1 - } - return 0 -} diff --git a/outputs/outputs.go b/outputs/outputs.go index f8e04f4b1..96206bc5f 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -40,7 +40,6 @@ var ( "structured": &Structured{}, "tap": &Tap{}, "silent": &Silent{}, - "bench": &Bench{}, } foPerfData = "perfdata" foVerbose = "verbose" diff --git a/outputs/rspecish.go b/outputs/rspecish.go index d56b6a902..5bc68c71d 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -31,6 +31,9 @@ func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, for resultGroup := range results { failedOrSkippedGroup := []resource.TestResult{} for _, testResult := range resultGroup { + // Calculates the start and end times based on the start of the first test + // and end of the last test, this allows the time/duration to be stable + // FIXME: move this to shared code if startTime.IsZero() || testResult.StartTime.Before(startTime) { startTime = testResult.StartTime } From a77d2bbd967794bbda3fcb2a00d83b3729a8d234 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 17 Jul 2023 07:28:32 -0700 Subject: [PATCH 38/41] Changed: include_raw by default, provide exclude_raw as a format flag --- outputs/documentation.go | 2 +- outputs/json.go | 2 +- outputs/junit.go | 2 +- outputs/nagios.go | 2 +- outputs/outputs.go | 2 +- outputs/rspecish.go | 2 +- outputs/structured.go | 2 +- outputs/tap.go | 2 +- ...ching_transformers_failing.1.documentation | 60 +++++++++++++++++++ ...t_matching_transformers_failing.1.rspecish | 30 ++++++++++ .../out_matching_transformers_failing.1.tap | 30 +++++----- 11 files changed, 113 insertions(+), 23 deletions(-) diff --git a/outputs/documentation.go b/outputs/documentation.go index d1e916003..7186ff33a 100644 --- a/outputs/documentation.go +++ b/outputs/documentation.go @@ -19,7 +19,7 @@ func (r Documentation) ValidOptions() []*formatOption { func (r Documentation) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + includeRaw := !util.IsValueInList(foExcludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) diff --git a/outputs/json.go b/outputs/json.go index cc89cfd0d..88354b438 100644 --- a/outputs/json.go +++ b/outputs/json.go @@ -26,7 +26,7 @@ func (r Json) Output(w io.Writer, results <-chan []resource.TestResult, var pretty bool pretty = util.IsValueInList(foPretty, outConfig.FormatOptions) - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + includeRaw := !util.IsValueInList(foExcludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) diff --git a/outputs/junit.go b/outputs/junit.go index 69782e3e4..dc547d2be 100644 --- a/outputs/junit.go +++ b/outputs/junit.go @@ -23,7 +23,7 @@ func (r JUnit) ValidOptions() []*formatOption { func (r JUnit) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + includeRaw := !util.IsValueInList(foExcludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) diff --git a/outputs/nagios.go b/outputs/nagios.go index 3fe157f3b..d91776dba 100644 --- a/outputs/nagios.go +++ b/outputs/nagios.go @@ -27,7 +27,7 @@ func (r Nagios) Output(w io.Writer, results <-chan []resource.TestResult, var perfdata, verbose bool perfdata = util.IsValueInList(foPerfData, outConfig.FormatOptions) verbose = util.IsValueInList(foVerbose, outConfig.FormatOptions) - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + includeRaw := !util.IsValueInList(foExcludeRaw, outConfig.FormatOptions) var startTime time.Time var endTime time.Time diff --git a/outputs/outputs.go b/outputs/outputs.go index 96206bc5f..c4e8d2ece 100644 --- a/outputs/outputs.go +++ b/outputs/outputs.go @@ -44,7 +44,7 @@ var ( foPerfData = "perfdata" foVerbose = "verbose" foPretty = "pretty" - foIncludeRaw = "include_raw" + foExcludeRaw = "exclude_raw" foSort = "sort" ) diff --git a/outputs/rspecish.go b/outputs/rspecish.go index 5bc68c71d..8c3402602 100644 --- a/outputs/rspecish.go +++ b/outputs/rspecish.go @@ -63,7 +63,7 @@ func (r Rspecish) Output(w io.Writer, results <-chan []resource.TestResult, } fmt.Fprint(w, "\n\n") - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + includeRaw := !util.IsValueInList(foExcludeRaw, outConfig.FormatOptions) fmt.Fprint(w, failedOrSkippedSummary(failedOrSkipped, includeRaw)) diff --git a/outputs/structured.go b/outputs/structured.go index 8b95db8cc..ce988e68a 100644 --- a/outputs/structured.go +++ b/outputs/structured.go @@ -48,7 +48,7 @@ func (s *StructureTestSummary) String() string { // Output processes output from tests into StructuredOutput written to w as a string func (r Structured) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + includeRaw := !util.IsValueInList(foExcludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) diff --git a/outputs/tap.go b/outputs/tap.go index 5c105f5e3..341e2a0ef 100644 --- a/outputs/tap.go +++ b/outputs/tap.go @@ -19,7 +19,7 @@ func (r Tap) ValidOptions() []*formatOption { func (r Tap) Output(w io.Writer, results <-chan []resource.TestResult, outConfig util.OutputConfig) (exitCode int) { - includeRaw := util.IsValueInList(foIncludeRaw, outConfig.FormatOptions) + includeRaw := !util.IsValueInList(foExcludeRaw, outConfig.FormatOptions) sort := util.IsValueInList(foSort, outConfig.FormatOptions) results = getResults(results, sort) diff --git a/testdata/out_matching_transformers_failing.1.documentation b/testdata/out_matching_transformers_failing.1.documentation index a70b86637..c80967b3d 100644 --- a/testdata/out_matching_transformers_failing.1.documentation +++ b/testdata/out_matching_transformers_failing.1.documentation @@ -5,6 +5,8 @@ to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] +the raw value was + "foo bar\nmoo cow\n" Matching: test_array: matches: Expected ["45","46","47"] @@ -19,16 +21,22 @@ to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" Matching: test_gjson_invalid: matches: Error matchers.Gjson{Path:"@this"}: Invalid json the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"arr\"" Matching: test_gjson_not_found: matches: Error matchers.Gjson{Path:"foo"}: Path not found: foo the transform chain was [{"gjson":{"Path":"foo"}}] +the raw value was + "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} @@ -36,6 +44,8 @@ to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_count: matches: Expected 15 @@ -43,6 +53,8 @@ to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_prefix: matches: Expected "bar" @@ -50,6 +62,8 @@ to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_this: matches: Expected {"count":"15","foo":"bar","moo":{"nested":"cow"}} @@ -57,6 +71,8 @@ to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_not_key: matches: Expected {"nested":"cow"} @@ -64,6 +80,8 @@ not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_simple: matches: Expected "cow" @@ -71,6 +89,8 @@ to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_using_this_and_equal: matches: Expected {"baz":"bing","foo":"bar"} @@ -78,6 +98,8 @@ to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"foo\": \"bar\", \"baz\": \"bing\"}" Matching: test_numeric_string: matches: Expected "128" @@ -85,6 +107,8 @@ to equal "129" the transform chain was [{"to-string":{}}] +the raw value was + 128 Matching: test_reader_as_single_string: matches: Expected "cool" @@ -97,6 +121,8 @@ to be numerically le 20 the transform chain was [{"to-numeric":{}}] +the raw value was + "40" Matching: test_reader_using_string_matchers: matches: Expected "foo bar\n15\nmoo cow\n" @@ -109,6 +135,8 @@ to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] +the raw value was + "128.3" Matching: test_string_numeric: matches: Expected 128 @@ -116,6 +144,8 @@ to be numerically eq 129 the transform chain was [{"to-numeric":{}}] +the raw value was + "128" Failures/Skipped: @@ -127,6 +157,8 @@ to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] +the raw value was + "foo bar\nmoo cow\n" Matching: test_array: matches: Expected @@ -143,18 +175,24 @@ to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" Matching: test_gjson_invalid: matches: Error matchers.Gjson{Path:"@this"}: Invalid json the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"arr\"" Matching: test_gjson_not_found: matches: Error matchers.Gjson{Path:"foo"}: Path not found: foo the transform chain was [{"gjson":{"Path":"foo"}}] +the raw value was + "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" Matching: test_gjson_transform_nested_and: matches: Expected @@ -163,6 +201,8 @@ to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_count: matches: Expected @@ -171,6 +211,8 @@ to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_prefix: matches: Expected @@ -179,6 +221,8 @@ to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_this: matches: Expected @@ -187,6 +231,8 @@ to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_not_key: matches: Expected @@ -195,6 +241,8 @@ not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_simple: matches: Expected @@ -203,6 +251,8 @@ to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_using_this_and_equal: matches: Expected @@ -211,6 +261,8 @@ to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"foo\": \"bar\", \"baz\": \"bing\"}" Matching: test_numeric_string: matches: Expected @@ -219,6 +271,8 @@ to equal "129" the transform chain was [{"to-string":{}}] +the raw value was + 128 Matching: test_reader_as_single_string: matches: Expected @@ -233,6 +287,8 @@ to be numerically le 20 the transform chain was [{"to-numeric":{}}] +the raw value was + "40" Matching: test_reader_using_string_matchers: matches: Expected @@ -247,6 +303,8 @@ to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] +the raw value was + "128.3" Matching: test_string_numeric: matches: Expected @@ -255,6 +313,8 @@ to be numerically eq 129 the transform chain was [{"to-numeric":{}}] +the raw value was + "128" Total Duration: Count: 18, Failed: 18, Skipped: 0 diff --git a/testdata/out_matching_transformers_failing.1.rspecish b/testdata/out_matching_transformers_failing.1.rspecish index 9994030f8..273492d6b 100644 --- a/testdata/out_matching_transformers_failing.1.rspecish +++ b/testdata/out_matching_transformers_failing.1.rspecish @@ -9,6 +9,8 @@ to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] +the raw value was + "foo bar\nmoo cow\n" Matching: test_array: matches: Expected @@ -25,18 +27,24 @@ to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" Matching: test_gjson_invalid: matches: Error matchers.Gjson{Path:"@this"}: Invalid json the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"arr\"" Matching: test_gjson_not_found: matches: Error matchers.Gjson{Path:"foo"}: Path not found: foo the transform chain was [{"gjson":{"Path":"foo"}}] +the raw value was + "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" Matching: test_gjson_transform_nested_and: matches: Expected @@ -45,6 +53,8 @@ to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_count: matches: Expected @@ -53,6 +63,8 @@ to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_prefix: matches: Expected @@ -61,6 +73,8 @@ to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_nested_this: matches: Expected @@ -69,6 +83,8 @@ to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_not_key: matches: Expected @@ -77,6 +93,8 @@ not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_transform_simple: matches: Expected @@ -85,6 +103,8 @@ to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] +the raw value was + "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" Matching: test_gjson_using_this_and_equal: matches: Expected @@ -93,6 +113,8 @@ to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] +the raw value was + "{\"foo\": \"bar\", \"baz\": \"bing\"}" Matching: test_numeric_string: matches: Expected @@ -101,6 +123,8 @@ to equal "129" the transform chain was [{"to-string":{}}] +the raw value was + 128 Matching: test_reader_as_single_string: matches: Expected @@ -115,6 +139,8 @@ to be numerically le 20 the transform chain was [{"to-numeric":{}}] +the raw value was + "40" Matching: test_reader_using_string_matchers: matches: Expected @@ -129,6 +155,8 @@ to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] +the raw value was + "128.3" Matching: test_string_numeric: matches: Expected @@ -137,6 +165,8 @@ to be numerically eq 129 the transform chain was [{"to-numeric":{}}] +the raw value was + "128" Total Duration: Count: 18, Failed: 18, Skipped: 0 diff --git a/testdata/out_matching_transformers_failing.1.tap b/testdata/out_matching_transformers_failing.1.tap index 20f3a5c69..851a8c6ac 100644 --- a/testdata/out_matching_transformers_failing.1.tap +++ b/testdata/out_matching_transformers_failing.1.tap @@ -1,19 +1,19 @@ 1..18 -not ok 1 - Matching: basic_reader_as_array: matches: Expected ["foo bar","moo cow",""] to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] +not ok 1 - Matching: basic_reader_as_array: matches: Expected ["foo bar","moo cow",""] to contain element matching {"contain-substring":"fox"} the transform chain was [{"to-array":{}}] the raw value was "foo bar\nmoo cow\n" not ok 2 - Matching: test_array: matches: Expected ["45","46","47"] to contain elements [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] the missing elements were [{"contain-element":{"match-regexp":"5."}},"55",{"and":[{"ge":56},{"le":30}]}] -not ok 3 - Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] -not ok 4 - Matching: test_gjson_invalid: matches: Error matchers.Gjson{Path:"@this"}: Invalid json the transform chain was [{"gjson":{"Path":"@this"}}] -not ok 5 - Matching: test_gjson_not_found: matches: Error matchers.Gjson{Path:"foo"}: Path not found: foo the transform chain was [{"gjson":{"Path":"foo"}}] -not ok 6 - Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] -not ok 7 - Matching: test_gjson_transform_nested_count: matches: Expected 15 to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] -not ok 8 - Matching: test_gjson_transform_nested_prefix: matches: Expected "bar" to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] -not ok 9 - Matching: test_gjson_transform_nested_this: matches: Expected {"count":"15","foo":"bar","moo":{"nested":"cow"}} to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] -not ok 10 - Matching: test_gjson_transform_not_key: matches: Expected {"nested":"cow"} not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] -not ok 11 - Matching: test_gjson_transform_simple: matches: Expected "cow" to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] -not ok 12 - Matching: test_gjson_using_this_and_equal: matches: Expected {"baz":"bing","foo":"bar"} to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] -not ok 13 - Matching: test_numeric_string: matches: Expected "128" to equal "129" the transform chain was [{"to-string":{}}] +not ok 3 - Matching: test_gjson_have_key_array: matches: Expected {"arr":[{"nested":"cow"},{"nested2":"moo"}]} to satisfy at least one of these matchers [{"have-key":"fail"}] the transform chain was [{"gjson":{"Path":"@this"}}] the raw value was "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" +not ok 4 - Matching: test_gjson_invalid: matches: Error matchers.Gjson{Path:"@this"}: Invalid json the transform chain was [{"gjson":{"Path":"@this"}}] the raw value was "{\"arr\"" +not ok 5 - Matching: test_gjson_not_found: matches: Error matchers.Gjson{Path:"foo"}: Path not found: foo the transform chain was [{"gjson":{"Path":"foo"}}] the raw value was "{\"arr\": [{\"nested\": \"cow\"}, {\"nested2\": \"moo\"}]}" +not ok 6 - Matching: test_gjson_transform_nested_and: matches: Expected {"nested":"cow"} to have key matching "nope" the transform chain was [{"gjson":{"Path":"moo"}}] the raw value was "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" +not ok 7 - Matching: test_gjson_transform_nested_count: matches: Expected 15 to be numerically le 10 the transform chain was [{"gjson":{"Path":"count"}},{"to-numeric":{}}] the raw value was "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" +not ok 8 - Matching: test_gjson_transform_nested_prefix: matches: Expected "bar" to have prefix "x" the transform chain was [{"gjson":{"Path":"foo"}}] the raw value was "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" +not ok 9 - Matching: test_gjson_transform_nested_this: matches: Expected {"count":"15","foo":"bar","moo":{"nested":"cow"}} to have key matching "nope" the transform chain was [{"gjson":{"Path":"@this"}}] the raw value was "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" +not ok 10 - Matching: test_gjson_transform_not_key: matches: Expected {"nested":"cow"} not to have key matching "nested" the transform chain was [{"gjson":{"Path":"moo"}}] the raw value was "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" +not ok 11 - Matching: test_gjson_transform_simple: matches: Expected "cow" to equal "cowx" the transform chain was [{"gjson":{"Path":"moo.nested"}}] the raw value was "{\"foo\": \"bar\", \"moo\": {\"nested\": \"cow\"}, \"count\": \"15\"}" +not ok 12 - Matching: test_gjson_using_this_and_equal: matches: Expected {"baz":"bing","foo":"bar"} to equal {"baz":"bing","fox":"bar"} the transform chain was [{"gjson":{"Path":"@this"}}] the raw value was "{\"foo\": \"bar\", \"baz\": \"bing\"}" +not ok 13 - Matching: test_numeric_string: matches: Expected "128" to equal "129" the transform chain was [{"to-string":{}}] the raw value was 128 not ok 14 - Matching: test_reader_as_single_string: matches: Expected "cool" to equal "not-cool" -not ok 15 - Matching: test_reader_using_int_matchers: matches: Expected 40 to be numerically le 20 the transform chain was [{"to-numeric":{}}] +not ok 15 - Matching: test_reader_using_int_matchers: matches: Expected 40 to be numerically le 20 the transform chain was [{"to-numeric":{}}] the raw value was "40" not ok 16 - Matching: test_reader_using_string_matchers: matches: Expected "foo bar\n15\nmoo cow\n" to have length 15 -not ok 17 - Matching: test_string_float: matches: Expected 128.3 to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] -not ok 18 - Matching: test_string_numeric: matches: Expected 128 to be numerically eq 129 the transform chain was [{"to-numeric":{}}] +not ok 17 - Matching: test_string_float: matches: Expected 128.3 to be numerically eq 129.3 the transform chain was [{"to-numeric":{}}] the raw value was "128.3" +not ok 18 - Matching: test_string_numeric: matches: Expected 128 to be numerically eq 129 the transform chain was [{"to-numeric":{}}] the raw value was "128" From a41dac2c1b1760ee0caf0f87a76a67476d0d6d82 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 17 Jul 2023 10:27:56 -0700 Subject: [PATCH 39/41] Changed: go-funk -> lo --- go.mod | 3 ++- go.sum | 6 ++++-- system/mount.go | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 34055d825..685486c92 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/prometheus/client_golang v1.7.1 github.com/prometheus/common v0.10.0 + github.com/samber/lo v1.38.1 github.com/stretchr/testify v1.8.1 - github.com/thoas/go-funk v0.9.3 github.com/tidwall/gjson v1.14.4 github.com/urfave/cli v1.22.12 gopkg.in/yaml.v2 v2.4.0 @@ -54,6 +54,7 @@ require ( github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect golang.org/x/crypto v0.7.0 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.6.0 // indirect diff --git a/go.sum b/go.sum index be4e5765b..7a4f00169 100644 --- a/go.sum +++ b/go.sum @@ -141,6 +141,8 @@ github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= @@ -163,8 +165,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= -github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -180,6 +180,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= diff --git a/system/mount.go b/system/mount.go index 45668bb5c..557dcbbcd 100644 --- a/system/mount.go +++ b/system/mount.go @@ -6,7 +6,7 @@ import ( "github.com/goss-org/goss/util" "github.com/moby/sys/mountinfo" - "github.com/thoas/go-funk" + "github.com/samber/lo" ) type Mount interface { @@ -80,7 +80,7 @@ func (m *DefMount) Opts() ([]string, error) { } allOpts := splitMountInfo(strings.Join([]string{m.mountInfo.Options, m.mountInfo.VFSOptions}, ",")) - return funk.UniqString(allOpts), nil + return lo.Uniq(allOpts), nil } func (m *DefMount) Source() (string, error) { From d178d97e1d103a6c44b7d34f43774119c0b25092 Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Mon, 17 Jul 2023 10:28:25 -0700 Subject: [PATCH 40/41] Changed: temp fix to deal with httpbin.org slowness. Need to move to offline testing to avoid flakiness --- integration-tests/goss/goss-shared.yaml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/integration-tests/goss/goss-shared.yaml b/integration-tests/goss/goss-shared.yaml index c3db8c764..ce5a1f5e7 100644 --- a/integration-tests/goss/goss-shared.yaml +++ b/integration-tests/goss/goss-shared.yaml @@ -191,12 +191,13 @@ http: {{ if index .Vars .Env.OS "proxy" }} https://httpbin.org/anything: status: 200 + timeout: 60000 proxy: {{ index .Vars .Env.OS "proxy" }} {{ end }} https://httpbin.org/headers: status: 200 allow-insecure: false - timeout: 5000 + timeout: 60000 request-headers: - "Foo: bar" headers: ["Content-Type: application/json"] @@ -204,7 +205,7 @@ http: https://httpbin.org/headers?host: status: 200 allow-insecure: false - timeout: 5000 + timeout: 60000 request-headers: # This is causing intermittent errors depending on the httpbin server hit # need to see if there's a good way around this, maybe local httpbin? @@ -215,26 +216,31 @@ http: body: ['"Host": "httpbin.org"'] https://httpbin.org/basic-auth/username/secret: status: 200 + timeout: 60000 username: username password: secret https://httpbin.org/basic-auth/username/secret?failure: status: 401 + timeout: 60000 username: username password: wrong https://httpbin.org/put: status: 200 method: PUT + timeout: 60000 request-body: '{"key": "value"}' body: - '"key": "value"' anything-with-get: url: https://httpbin.org/anything status: 200 + timeout: 60000 body: [] anything-with-put: url: https://httpbin.org/anything status: 200 method: GET + timeout: 60000 request-body: "request-body" body: ["request-body"] matching: From 783a2b407e45ca7dcd9560cee7e2ba9ac28b1dad Mon Sep 17 00:00:00 2001 From: Ahmed Elsabbahy Date: Tue, 18 Jul 2023 09:42:43 -0700 Subject: [PATCH 41/41] Locking down version in install.sh, to avoid RC being installed --- install.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index d1c8d8c26..365a8be3b 100644 --- a/install.sh +++ b/install.sh @@ -3,8 +3,9 @@ { set -e -LATEST_URL="https://github.com/goss-org/goss/releases/latest" -LATEST_EFFECTIVE=$(curl -s -L -o /dev/null ${LATEST_URL} -w '%{url_effective}') +# LATEST_URL="https://github.com/goss-org/goss/releases/latest" +# LATEST_EFFECTIVE=$(curl -s -L -o /dev/null ${LATEST_URL} -w '%{url_effective}') +LATEST_EFFECTIVE=https://github.com/goss-org/goss/releases/tag/v0.3.23 LATEST=${LATEST_EFFECTIVE##*/} DGOSS_VER=$GOSS_VER