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 (
"fmt"
"io"
"os"
"strings"
"github.com/gosuri/uitable"
@ -54,6 +55,7 @@ type searchHubOptions struct {
maxColWidth uint
outputFormat output.Format
listRepoURL bool
noResultsFail bool
}
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.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.BoolVarP(&o.noResultsFail, "fail-if-no-results-found", "f", false, "exit with status code 1 if no results found")
bindOutputFlag(cmd, &o.outputFormat)
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 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 {
@ -108,12 +110,13 @@ type hubChartElement struct {
}
type hubSearchWriter struct {
elements []hubChartElement
columnWidth uint
listRepoURL bool
elements []hubChartElement
columnWidth uint
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
for _, r := range results {
// 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}})
}
return &hubSearchWriter{elements, columnWidth, listRepoURL}
return &hubSearchWriter{elements, columnWidth, listRepoURL, noResultsFail}
}
func (h *hubSearchWriter) WriteTable(out io.Writer) error {
@ -135,6 +138,9 @@ func (h *hubSearchWriter) WriteTable(out io.Writer) error {
if err != nil {
return fmt.Errorf("unable to write results: %s", err)
}
if h.noResultsFail {
os.Exit(1)
}
return nil
}
table := uitable.New()

@ -17,9 +17,12 @@ limitations under the License.
package main
import (
"bytes"
"fmt"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"testing"
)
@ -90,3 +93,58 @@ func TestSearchHubOutputCompletion(t *testing.T) {
func TestSearchHubFileCompletion(t *testing.T) {
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())
}
}

@ -64,14 +64,15 @@ Repositories are managed with 'helm repo' commands.
const searchMaxScore = 25
type searchRepoOptions struct {
versions bool
regexp bool
devel bool
version string
maxColWidth uint
repoFile string
repoCacheDir string
outputFormat output.Format
versions bool
regexp bool
devel bool
version string
maxColWidth uint
repoFile string
repoCacheDir string
outputFormat output.Format
noResultsFail bool
}
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.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.BoolVarP(&o.noResultsFail, "fail-if-no-results-found", "f", false, "exit with status code 1 if no results found")
bindOutputFlag(cmd, &o.outputFormat)
return cmd
@ -124,7 +126,7 @@ func (o *searchRepoOptions) run(out io.Writer, args []string) error {
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() {
@ -205,8 +207,9 @@ type repoChartElement struct {
}
type repoSearchWriter struct {
results []*search.Result
columnWidth uint
results []*search.Result
columnWidth uint
noResultsFail bool
}
func (r *repoSearchWriter) WriteTable(out io.Writer) error {
@ -215,6 +218,9 @@ func (r *repoSearchWriter) WriteTable(out io.Writer) error {
if err != nil {
return fmt.Errorf("unable to write results: %s", err)
}
if r.noResultsFail {
os.Exit(1)
}
return nil
}
table := uitable.New()

@ -17,6 +17,9 @@ limitations under the License.
package main
import (
"bytes"
"os"
"os/exec"
"testing"
)
@ -91,3 +94,50 @@ func TestSearchRepoOutputCompletion(t *testing.T) {
func TestSearchRepoFileCompletion(t *testing.T) {
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