diff --git a/src/cloud/api/BUILD.bazel b/src/cloud/api/BUILD.bazel index 2401570651c..d642656a604 100644 --- a/src/cloud/api/BUILD.bazel +++ b/src/cloud/api/BUILD.bazel @@ -40,6 +40,7 @@ go_library( "//src/shared/services/server", "//src/utils/script", "@com_github_gorilla_handlers//:handlers", + "@com_github_olivere_elastic_v7//:elastic", "@com_github_sirupsen_logrus//:logrus", "@com_github_spf13_pflag//:pflag", "@com_github_spf13_viper//:viper", diff --git a/src/cloud/api/api_server.go b/src/cloud/api/api_server.go index 87f68ec76c2..7d2b1cd42c7 100644 --- a/src/cloud/api/api_server.go +++ b/src/cloud/api/api_server.go @@ -28,6 +28,7 @@ import ( "time" "github.com/gorilla/handlers" + "github.com/olivere/elastic/v7" log "github.com/sirupsen/logrus" "github.com/spf13/pflag" "github.com/spf13/viper" @@ -69,6 +70,7 @@ func init() { pflag.String("auth_connector_name", "", "If any, the name of the auth connector to be used with Pixie") pflag.String("auth_connector_callback_url", "", "If any, the callback URL for the auth connector") pflag.Bool("script_modification_disabled", false, "If script modification should be disallowed to prevent arbitrary script execution") + pflag.Bool("disable_autocomplete", false, "Disable autocomplete functionality (no Elasticsearch required)") } func main() { @@ -136,15 +138,20 @@ func main() { // Connect to NATS. nc := msgbus.MustConnectNATS() - esConfig := &esutils.Config{ - URL: []string{viper.GetString("elastic_service")}, - User: viper.GetString("elastic_username"), - Passwd: viper.GetString("elastic_password"), - CaCertFile: viper.GetString("elastic_ca_cert"), - } - es, err := esutils.NewEsClient(esConfig) - if err != nil { - log.WithError(err).Fatal("Could not connect to elastic") + disableAutocomplete := viper.GetBool("disable_autocomplete") + + var es *elastic.Client + if !disableAutocomplete { + esConfig := &esutils.Config{ + URL: []string{viper.GetString("elastic_service")}, + User: viper.GetString("elastic_username"), + Passwd: viper.GetString("elastic_password"), + CaCertFile: viper.GetString("elastic_ca_cert"), + } + es, err = esutils.NewEsClient(esConfig) + if err != nil { + log.WithError(err).Fatal("Could not connect to elastic") + } } mux := http.NewServeMux() @@ -228,45 +235,52 @@ func main() { vizierpb.RegisterVizierServiceServer(s.GRPCServer(), vpt) vizierpb.RegisterVizierDebugServiceServer(s.GRPCServer(), vpt) - mdIndexName := viper.GetString("md_index_name") - if mdIndexName == "" { - log.Fatal("Must specify a name for the elastic index.") - } - esSuggester, err := autocomplete.NewElasticSuggester(es, mdIndexName, "scripts", pc) - if err != nil { - log.WithError(err).Fatal("Failed to start elastic suggester") - } - - var br *script.BundleManager - var bundleErr error - updateBundle := func() { - // Requiring the bundle manager in the API service is temporary until we - // start indexing scripts. - br, bundleErr = script.NewBundleManagerWithOrg([]string{defaultBundleFile, ossBundleFile}, "", "") - if bundleErr != nil { - log.WithError(bundleErr).Error("Failed to init bundle manager") - br = nil + var suggester autocomplete.Suggester + if disableAutocomplete { + log.Info("Autocomplete disabled - using NoopSuggester") + suggester = autocomplete.NewNoopSuggester() + } else { + mdIndexName := viper.GetString("md_index_name") + if mdIndexName == "" { + log.Fatal("Must specify a name for the elastic index.") } - esSuggester.UpdateScriptBundle(br) - } - - quitCh := make(chan bool) - go func() { - updateBundle() - scriptTimer := time.NewTicker(30 * time.Second) - defer scriptTimer.Stop() - for { - select { - case <-quitCh: - return - case <-scriptTimer.C: - updateBundle() + esSuggester, err := autocomplete.NewElasticSuggester(es, mdIndexName, "scripts", pc) + if err != nil { + log.WithError(err).Fatal("Failed to start elastic suggester") + } + suggester = esSuggester + + var br *script.BundleManager + var bundleErr error + updateBundle := func() { + // Requiring the bundle manager in the API service is temporary until we + // start indexing scripts. + br, bundleErr = script.NewBundleManagerWithOrg([]string{defaultBundleFile, ossBundleFile}, "", "") + if bundleErr != nil { + log.WithError(bundleErr).Error("Failed to init bundle manager") + br = nil } + esSuggester.UpdateScriptBundle(br) } - }() - defer close(quitCh) - as := &controllers.AutocompleteServer{Suggester: esSuggester} + quitCh := make(chan bool) + go func() { + updateBundle() + scriptTimer := time.NewTicker(30 * time.Second) + defer scriptTimer.Stop() + for { + select { + case <-quitCh: + return + case <-scriptTimer.C: + updateBundle() + } + } + }() + defer close(quitCh) + } + + as := &controllers.AutocompleteServer{Suggester: suggester} cloudpb.RegisterAutocompleteServiceServer(s.GRPCServer(), as) os := &controllers.OrganizationServiceServer{ProfileServiceClient: pc, AuthServiceClient: ac, OrgServiceClient: oc} diff --git a/src/cloud/autocomplete/BUILD.bazel b/src/cloud/autocomplete/BUILD.bazel index e371401469a..790743aaa6d 100644 --- a/src/cloud/autocomplete/BUILD.bazel +++ b/src/cloud/autocomplete/BUILD.bazel @@ -21,6 +21,7 @@ go_library( name = "autocomplete", srcs = [ "autocomplete.go", + "noop_suggester.go", "suggester.go", ], importpath = "px.dev/pixie/src/cloud/autocomplete", diff --git a/src/cloud/autocomplete/noop_suggester.go b/src/cloud/autocomplete/noop_suggester.go new file mode 100644 index 00000000000..be9b686015e --- /dev/null +++ b/src/cloud/autocomplete/noop_suggester.go @@ -0,0 +1,41 @@ +/* + * Copyright 2018- The Pixie Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package autocomplete + +// NoopSuggester is a suggester that returns empty results. +// It is used when autocomplete is disabled (e.g., when Elasticsearch is not available). +type NoopSuggester struct{} + +// NewNoopSuggester creates a new NoopSuggester. +func NewNoopSuggester() *NoopSuggester { + return &NoopSuggester{} +} + +// GetSuggestions returns empty results for all requests. +func (n *NoopSuggester) GetSuggestions(reqs []*SuggestionRequest) ([]*SuggestionResult, error) { + results := make([]*SuggestionResult, len(reqs)) + for i := range reqs { + results[i] = &SuggestionResult{ + Suggestions: []*Suggestion{}, + ExactMatch: false, + HasAdditionalMatches: false, + } + } + return results, nil +}