diff --git a/Gopkg.lock b/Gopkg.lock index 6261b82f06..8fa22facf8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -856,6 +856,12 @@ ] revision = "ef147856a6ddbb60760db74283d2424e98c87bff" +[[projects]] + branch = "master" + name = "golang.org/x/sync" + packages = ["errgroup"] + revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" + [[projects]] branch = "master" name = "golang.org/x/sys" @@ -1280,6 +1286,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "e8840696252add0e84db6bf9377dc0aea33b1233ed218a984aa68dde945f5024" + inputs-digest = "74d0700959840117869d226bcaab2b82b155f4a4be42becf655c570c7dece1ca" solver-name = "gps-cdcl" solver-version = 1 diff --git a/pkg/jx/cmd/compliance_results.go b/pkg/jx/cmd/compliance_results.go index afaded27e6..aec8ee3e96 100644 --- a/pkg/jx/cmd/compliance_results.go +++ b/pkg/jx/cmd/compliance_results.go @@ -10,12 +10,14 @@ import ( "github.com/heptio/sonobuoy/pkg/client" "github.com/heptio/sonobuoy/pkg/client/results" + "github.com/heptio/sonobuoy/pkg/plugin/aggregation" "github.com/jenkins-x/jx/pkg/jx/cmd/templates" "github.com/jenkins-x/jx/pkg/log" "github.com/jenkins-x/jx/pkg/util" "github.com/onsi/ginkgo/reporters" "github.com/pkg/errors" "github.com/spf13/cobra" + "golang.org/x/sync/errgroup" ) var ( @@ -68,45 +70,52 @@ func (o *ComplianceResultsOptions) Run() error { return errors.Wrap(err, "could not create the compliance client") } + status, err := cc.GetStatus(complianceNamespace) + if err != nil { + return errors.Wrap(err, "failed to retrieve the compliance status") + } + + if status.Status != aggregation.CompleteStatus { + log.Info("Compliance results not ready. Run `jx compliance status` for status.") + return nil + } + cfg := &client.RetrieveConfig{ Namespace: complianceNamespace, } reader, errch := cc.RetrieveResults(cfg) - - // TODO Refactor? Seems like there should be a nicer way to handle this... - go func() { - for { - select { - case err := <-errch: - if err != nil { - log.Fatalf("could not retrieve the compliance results\n %v", err) - o.Exit(1) - } - } + eg := &errgroup.Group{} + eg.Go(func() error { return <-errch }) + eg.Go(func() error { + resultsReader, errch := untarResults(reader) + gzr, err := gzip.NewReader(resultsReader) + if err != nil { + return errors.Wrap(err, "could not create a gzip reader for compliance results ") } - }() - resultsReader, err := untarResults(reader) - if err != nil { - return errors.Wrap(err, "could not extract the compliance results from archive") - } - - gzr, err := gzip.NewReader(resultsReader) - if err != nil { - return errors.Wrap(err, "could not create a gzip reader for compliance results ") - } + testResults, err := cc.GetTests(gzr, "all") + if err != nil { + return errors.Wrap(err, "could not get the results of the compliance tests from the archive") + } + testResults = filterTests( + func(tc reporters.JUnitTestCase) bool { + return !results.Skipped(tc) + }, testResults) + sort.Sort(StatusSortedTestCases(testResults)) + o.printResults(testResults) + + err = <-errch + if err != nil { + return errors.Wrap(err, "could not extract the compliance results from archive") + } + return nil + }) - testResults, err := cc.GetTests(gzr, "all") + err = eg.Wait() if err != nil { - return errors.Wrap(err, "could not get the results of the compliance tests from the archive") + return errors.Wrap(err, "failed to retrieve the results") } - testResults = filterTests( - func(tc reporters.JUnitTestCase) bool { - return !results.Skipped(tc) - }, testResults) - sort.Sort(StatusSortedTestCases(testResults)) - o.printResults(testResults) return nil } @@ -156,27 +165,34 @@ func status(junitResult reporters.JUnitTestCase) string { } } -func untarResults(src io.Reader) (io.Reader, error) { +func untarResults(src io.Reader) (io.Reader, <-chan error) { + ec := make(chan error, 1) tarReader := tar.NewReader(src) for { header, err := tarReader.Next() if err != nil { if err != io.EOF { - return nil, err + ec <- err + return nil, ec + } else { + ec <- errors.New("no compliance results archive found") + return nil, ec } - break } if strings.HasSuffix(header.Name, ".tar.gz") { reader, writer := io.Pipe() - //TODO propagate the error out of the goroutine - go func(writer *io.PipeWriter) { + go func(writer *io.PipeWriter, ec chan error) { defer writer.Close() - io.Copy(writer, tarReader) - }(writer) + defer close(ec) + _, err := io.Copy(writer, tarReader) + if err != nil { + ec <- err + } + }(writer, ec) return reader, nil } } - return nil, errors.New("no compliance results archive found") + return nil, ec } func filterTests(predicate func(testCase reporters.JUnitTestCase) bool, testCases []reporters.JUnitTestCase) []reporters.JUnitTestCase { diff --git a/vendor/golang.org/x/sync/AUTHORS b/vendor/golang.org/x/sync/AUTHORS new file mode 100644 index 0000000000..15167cd746 --- /dev/null +++ b/vendor/golang.org/x/sync/AUTHORS @@ -0,0 +1,3 @@ +# This source code refers to The Go Authors for copyright purposes. +# The master list of authors is in the main Go distribution, +# visible at http://tip.golang.org/AUTHORS. diff --git a/vendor/golang.org/x/sync/CONTRIBUTORS b/vendor/golang.org/x/sync/CONTRIBUTORS new file mode 100644 index 0000000000..1c4577e968 --- /dev/null +++ b/vendor/golang.org/x/sync/CONTRIBUTORS @@ -0,0 +1,3 @@ +# This source code was written by the Go contributors. +# The master list of contributors is in the main Go distribution, +# visible at http://tip.golang.org/CONTRIBUTORS. diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE new file mode 100644 index 0000000000..6a66aea5ea --- /dev/null +++ b/vendor/golang.org/x/sync/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/golang.org/x/sync/PATENTS b/vendor/golang.org/x/sync/PATENTS new file mode 100644 index 0000000000..733099041f --- /dev/null +++ b/vendor/golang.org/x/sync/PATENTS @@ -0,0 +1,22 @@ +Additional IP Rights Grant (Patents) + +"This implementation" means the copyrightable works distributed by +Google as part of the Go project. + +Google hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) +patent license to make, have made, use, offer to sell, sell, import, +transfer and otherwise run, modify and propagate the contents of this +implementation of Go, where such license applies only to those patent +claims, both currently owned or controlled by Google and acquired in +the future, licensable by Google that are necessarily infringed by this +implementation of Go. This grant does not include claims that would be +infringed only as a consequence of further modification of this +implementation. If you or your agent or exclusive licensee institute or +order or agree to the institution of patent litigation against any +entity (including a cross-claim or counterclaim in a lawsuit) alleging +that this implementation of Go or any code incorporated within this +implementation of Go constitutes direct or contributory patent +infringement, or inducement of patent infringement, then any patent +rights granted to you under this License for this implementation of Go +shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go new file mode 100644 index 0000000000..533438d91c --- /dev/null +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -0,0 +1,67 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package errgroup provides synchronization, error propagation, and Context +// cancelation for groups of goroutines working on subtasks of a common task. +package errgroup + +import ( + "sync" + + "golang.org/x/net/context" +) + +// A Group is a collection of goroutines working on subtasks that are part of +// the same overall task. +// +// A zero Group is valid and does not cancel on error. +type Group struct { + cancel func() + + wg sync.WaitGroup + + errOnce sync.Once + err error +} + +// WithContext returns a new Group and an associated Context derived from ctx. +// +// The derived Context is canceled the first time a function passed to Go +// returns a non-nil error or the first time Wait returns, whichever occurs +// first. +func WithContext(ctx context.Context) (*Group, context.Context) { + ctx, cancel := context.WithCancel(ctx) + return &Group{cancel: cancel}, ctx +} + +// Wait blocks until all function calls from the Go method have returned, then +// returns the first non-nil error (if any) from them. +func (g *Group) Wait() error { + g.wg.Wait() + if g.cancel != nil { + g.cancel() + } + return g.err +} + +// Go calls the given function in a new goroutine. +// +// The first call to return a non-nil error cancels the group; its error will be +// returned by Wait. +func (g *Group) Go(f func() error) { + g.wg.Add(1) + + go func() { + defer g.wg.Done() + + if err := f(); err != nil { + g.errOnce.Do(func() { + g.err = err + if g.cancel != nil { + g.cancel() + } + }) + } + }() +}