pull/9750/merge
Baiju Muthukadan 3 years ago committed by GitHub
commit e5f49e9fa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -19,6 +19,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"os"
"strings" "strings"
"github.com/gosuri/uitable" "github.com/gosuri/uitable"
@ -54,6 +55,7 @@ type searchHubOptions struct {
maxColWidth uint maxColWidth uint
outputFormat output.Format outputFormat output.Format
listRepoURL bool listRepoURL bool
noResultsFail bool
} }
func newSearchHubCmd(out io.Writer) *cobra.Command { func newSearchHubCmd(out io.Writer) *cobra.Command {
@ -72,7 +74,7 @@ func newSearchHubCmd(out io.Writer) *cobra.Command {
f.StringVar(&o.searchEndpoint, "endpoint", "https://hub.helm.sh", "Hub instance to query for charts") f.StringVar(&o.searchEndpoint, "endpoint", "https://hub.helm.sh", "Hub instance to query for charts")
f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table") f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table")
f.BoolVar(&o.listRepoURL, "list-repo-url", false, "print charts repository URL") f.BoolVar(&o.listRepoURL, "list-repo-url", false, "print charts repository URL")
f.BoolVarP(&o.noResultsFail, "fail-if-no-results-found", "f", false, "exit with status code 1 if no results found")
bindOutputFlag(cmd, &o.outputFormat) bindOutputFlag(cmd, &o.outputFormat)
return cmd return cmd
@ -91,7 +93,7 @@ func (o *searchHubOptions) run(out io.Writer, args []string) error {
return fmt.Errorf("unable to perform search against %q", o.searchEndpoint) return fmt.Errorf("unable to perform search against %q", o.searchEndpoint)
} }
return o.outputFormat.Write(out, newHubSearchWriter(results, o.searchEndpoint, o.maxColWidth, o.listRepoURL)) return o.outputFormat.Write(out, newHubSearchWriter(results, o.searchEndpoint, o.maxColWidth, o.noResultsFail, o.listRepoURL))
} }
type hubChartRepo struct { type hubChartRepo struct {
@ -111,9 +113,10 @@ type hubSearchWriter struct {
elements []hubChartElement elements []hubChartElement
columnWidth uint columnWidth uint
listRepoURL bool listRepoURL bool
noResultsFail bool
} }
func newHubSearchWriter(results []monocular.SearchResult, endpoint string, columnWidth uint, listRepoURL bool) *hubSearchWriter { func newHubSearchWriter(results []monocular.SearchResult, endpoint string, columnWidth uint, listRepoURL, noResultsFail bool) *hubSearchWriter {
var elements []hubChartElement var elements []hubChartElement
for _, r := range results { for _, r := range results {
// Backwards compatibility for Monocular // Backwards compatibility for Monocular
@ -126,7 +129,7 @@ func newHubSearchWriter(results []monocular.SearchResult, endpoint string, colum
elements = append(elements, hubChartElement{url, r.Relationships.LatestChartVersion.Data.Version, r.Relationships.LatestChartVersion.Data.AppVersion, r.Attributes.Description, hubChartRepo{URL: r.Attributes.Repo.URL, Name: r.Attributes.Repo.Name}}) elements = append(elements, hubChartElement{url, r.Relationships.LatestChartVersion.Data.Version, r.Relationships.LatestChartVersion.Data.AppVersion, r.Attributes.Description, hubChartRepo{URL: r.Attributes.Repo.URL, Name: r.Attributes.Repo.Name}})
} }
return &hubSearchWriter{elements, columnWidth, listRepoURL} return &hubSearchWriter{elements, columnWidth, listRepoURL, noResultsFail}
} }
func (h *hubSearchWriter) WriteTable(out io.Writer) error { func (h *hubSearchWriter) WriteTable(out io.Writer) error {
@ -135,6 +138,9 @@ func (h *hubSearchWriter) WriteTable(out io.Writer) error {
if err != nil { if err != nil {
return fmt.Errorf("unable to write results: %s", err) return fmt.Errorf("unable to write results: %s", err)
} }
if h.noResultsFail {
os.Exit(1)
}
return nil return nil
} }
table := uitable.New() table := uitable.New()

@ -17,9 +17,12 @@ limitations under the License.
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"os"
"os/exec"
"testing" "testing"
) )
@ -90,3 +93,58 @@ func TestSearchHubOutputCompletion(t *testing.T) {
func TestSearchHubFileCompletion(t *testing.T) { func TestSearchHubFileCompletion(t *testing.T) {
checkFileCompletion(t, "search hub", true) // File completion may be useful when inputting a keyword checkFileCompletion(t, "search hub", true) // File completion may be useful when inputting a keyword
} }
func TestSearchHubCmdExitCode(t *testing.T) {
if os.Getenv("RUN_MAIN_FOR_TESTING") == "1" {
// Setup a mock search service
var searchResult = `{"data":[]}`
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, searchResult)
}))
defer ts.Close()
os.Args = []string{"helm", "search", "hub", "syzygy", "--endpoint", ts.URL, "--fail-if-no-results-found"}
// We DO call helm's main() here. So this looks like a normal `helm` process.
main()
// As main calls os.Exit, we never reach this line.
// But the test called this block of code catches and verifies the exit code.
return
}
// Do a second run of this specific test(TestPluginExitCode) with RUN_MAIN_FOR_TESTING=1 set,
// So that the second run is able to run main() and this first run can verify the exit status returned by that.
//
// This technique originates from https://talks.golang.org/2014/testing.slide#23.
cmd := exec.Command(os.Args[0], "-test.run=TestSearchHubCmdExitCode")
cmd.Env = append(
os.Environ(),
"RUN_MAIN_FOR_TESTING=1",
)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
cmd.Stdout = stdout
cmd.Stderr = stderr
err := cmd.Run()
exiterr, ok := err.(*exec.ExitError)
if !ok {
t.Fatalf("Unexpected error returned by os.Exit: %T", err)
}
expectedStdout := "No results found\n"
if stdout.String() != expectedStdout {
t.Errorf("Expected %q written to stdout: Got %q", expectedStdout, stdout.String())
}
if stderr.String() != "" {
t.Errorf("Expected no writes to stderr: Got %q", stderr.String())
}
if exiterr.ExitCode() != 1 {
t.Errorf("Expected exit code 1: Got %d", exiterr.ExitCode())
}
}

@ -72,6 +72,7 @@ type searchRepoOptions struct {
repoFile string repoFile string
repoCacheDir string repoCacheDir string
outputFormat output.Format outputFormat output.Format
noResultsFail bool
} }
func newSearchRepoCmd(out io.Writer) *cobra.Command { func newSearchRepoCmd(out io.Writer) *cobra.Command {
@ -94,6 +95,7 @@ func newSearchRepoCmd(out io.Writer) *cobra.Command {
f.BoolVar(&o.devel, "devel", false, "use development versions (alpha, beta, and release candidate releases), too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") f.BoolVar(&o.devel, "devel", false, "use development versions (alpha, beta, and release candidate releases), too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.StringVar(&o.version, "version", "", "search using semantic versioning constraints on repositories you have added") f.StringVar(&o.version, "version", "", "search using semantic versioning constraints on repositories you have added")
f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table") f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table")
f.BoolVarP(&o.noResultsFail, "fail-if-no-results-found", "f", false, "exit with status code 1 if no results found")
bindOutputFlag(cmd, &o.outputFormat) bindOutputFlag(cmd, &o.outputFormat)
return cmd return cmd
@ -124,7 +126,7 @@ func (o *searchRepoOptions) run(out io.Writer, args []string) error {
return err return err
} }
return o.outputFormat.Write(out, &repoSearchWriter{data, o.maxColWidth}) return o.outputFormat.Write(out, &repoSearchWriter{data, o.maxColWidth, o.noResultsFail})
} }
func (o *searchRepoOptions) setupSearchedVersion() { func (o *searchRepoOptions) setupSearchedVersion() {
@ -207,6 +209,7 @@ type repoChartElement struct {
type repoSearchWriter struct { type repoSearchWriter struct {
results []*search.Result results []*search.Result
columnWidth uint columnWidth uint
noResultsFail bool
} }
func (r *repoSearchWriter) WriteTable(out io.Writer) error { func (r *repoSearchWriter) WriteTable(out io.Writer) error {
@ -215,6 +218,9 @@ func (r *repoSearchWriter) WriteTable(out io.Writer) error {
if err != nil { if err != nil {
return fmt.Errorf("unable to write results: %s", err) return fmt.Errorf("unable to write results: %s", err)
} }
if r.noResultsFail {
os.Exit(1)
}
return nil return nil
} }
table := uitable.New() table := uitable.New()

@ -17,6 +17,9 @@ limitations under the License.
package main package main
import ( import (
"bytes"
"os"
"os/exec"
"testing" "testing"
) )
@ -91,3 +94,50 @@ func TestSearchRepoOutputCompletion(t *testing.T) {
func TestSearchRepoFileCompletion(t *testing.T) { func TestSearchRepoFileCompletion(t *testing.T) {
checkFileCompletion(t, "search repo", true) // File completion may be useful when inputting a keyword checkFileCompletion(t, "search repo", true) // File completion may be useful when inputting a keyword
} }
func TestSearchRepositoriesCmdExitCode(t *testing.T) {
if os.Getenv("RUN_MAIN_FOR_TESTING") == "1" {
os.Args = []string{"helm", "search", "repo", "syzygy", "--fail-if-no-results-found"}
// We DO call helm's main() here. So this looks like a normal `helm` process.
main()
// As main calls os.Exit, we never reach this line.
// But the test called this block of code catches and verifies the exit code.
return
}
// Do a second run of this specific test(TestPluginExitCode) with RUN_MAIN_FOR_TESTING=1 set,
// So that the second run is able to run main() and this first run can verify the exit status returned by that.
//
// This technique originates from https://talks.golang.org/2014/testing.slide#23.
cmd := exec.Command(os.Args[0], "-test.run=TestSearchRepositoriesCmdExitCode")
cmd.Env = append(
os.Environ(),
"RUN_MAIN_FOR_TESTING=1",
)
stdout := &bytes.Buffer{}
stderr := &bytes.Buffer{}
cmd.Stdout = stdout
cmd.Stderr = stderr
err := cmd.Run()
exiterr, ok := err.(*exec.ExitError)
if !ok {
t.Fatalf("Unexpected error returned by os.Exit: %T", err)
}
expectedStdout := "No results found\n"
if stdout.String() != expectedStdout {
t.Errorf("Expected %q written to stdout: Got %q", expectedStdout, stdout.String())
}
if stderr.String() != "" {
t.Errorf("Expected no writes to stderr: Got %q", stderr.String())
}
if exiterr.ExitCode() != 1 {
t.Errorf("Expected exit code 1: Got %d", exiterr.ExitCode())
}
}

Loading…
Cancel
Save