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 {
@ -108,12 +110,13 @@ type hubChartElement struct {
} }
type hubSearchWriter struct { 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())
}
}

@ -64,14 +64,15 @@ Repositories are managed with 'helm repo' commands.
const searchMaxScore = 25 const searchMaxScore = 25
type searchRepoOptions struct { type searchRepoOptions struct {
versions bool versions bool
regexp bool regexp bool
devel bool devel bool
version string version string
maxColWidth uint maxColWidth uint
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() {
@ -205,8 +207,9 @@ 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