diff --git a/cmd/osm-controller/osm-controller.go b/cmd/osm-controller/osm-controller.go index 0c302ec3bb..a99bea8ccb 100644 --- a/cmd/osm-controller/osm-controller.go +++ b/cmd/osm-controller/osm-controller.go @@ -5,6 +5,7 @@ import ( "encoding/json" "flag" "fmt" + "net/http" "os" "path" "strings" @@ -245,22 +246,34 @@ func main() { events.GenericEventRecorder().FatalEvent(err, events.InitializationError, "Error initializing ADS server") } - // Initialize function and HTTP health probes - funcProbes := []health.Probes{xdsServer} - httpProbes := getHTTPHealthProbes() - - // Initialize the http server and start it - httpServer := httpserver.NewHTTPServer(funcProbes, httpProbes, metricsstore.DefaultMetricsStore, constants.MetricsServerPort) - httpServer.Start() - if err := createControllerManagerForOSMResources(certManager); err != nil { events.GenericEventRecorder().FatalEvent(err, events.InitializationError, "Error creating controller manager to reconcile OSM resources") } - // Expose /debug endpoints and data only if the enableDebugServer flag is enabled + // Initialize OSM's http service server + httpServer := httpserver.NewHTTPServer(constants.OSMServicePort) + + // Health/Liveness probes + funcProbes := []health.Probes{xdsServer} + httpServer.AddHandlers(map[string]http.Handler{ + "/health/ready": health.ReadinessHandler(funcProbes, getHTTPHealthProbes()), + "/health/alive": health.LivenessHandler(funcProbes, getHTTPHealthProbes()), + }) + // Metrics + httpServer.AddHandler("/metrics", metricsstore.DefaultMetricsStore.Handler()) + // Version + httpServer.AddHandler("/version", version.GetVersionHandler()) + + // Start HTTP server + err = httpServer.Start() + if err != nil { + log.Fatal().Err(err).Msgf("Failed to start OSM metrics/probes HTTP server") + } + + // Create DebugServer and start its config event listener. + // Listener takes care to start and stop the debug server as appropriate debugConfig := debugger.NewDebugConfig(certDebugger, xdsServer, meshCatalog, kubeConfig, kubeClient, cfg, kubernetesClient) - debugServerInterface := httpserver.NewDebugHTTPServer(debugConfig, constants.DebugPort) - httpserver.RegisterDebugServer(debugServerInterface, cfg) + debugConfig.StartDebugServerConfigListener() <-stop log.Info().Msg("Goodbye!") diff --git a/cmd/osm-controller/osm-controller_test.go b/cmd/osm-controller/osm-controller_test.go index edbc70d852..dd77624b22 100644 --- a/cmd/osm-controller/osm-controller_test.go +++ b/cmd/osm-controller/osm-controller_test.go @@ -2,82 +2,17 @@ package main import ( "context" - "strconv" "testing" - "time" - - "github.com/pkg/errors" tassert "github.com/stretchr/testify/assert" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" testclient "k8s.io/client-go/kubernetes/fake" "github.com/openservicemesh/osm/pkg/certificate/providers/tresor" - "github.com/openservicemesh/osm/pkg/configurator" "github.com/openservicemesh/osm/pkg/constants" - "github.com/openservicemesh/osm/pkg/httpserver" -) - -const ( - testOSMNamespace = "-test-osm-namespace-" - testOSMConfigMapName = "-test-osm-config-map-" ) -func toggleDebugServer(enable bool, kubeClient *testclient.Clientset) error { - updatedConfigMap := v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: testOSMNamespace, - Name: testOSMConfigMapName, - }, - Data: map[string]string{ - "enable_debug_server": strconv.FormatBool(enable), - }, - } - _, err := kubeClient.CoreV1().ConfigMaps(testOSMNamespace).Update(context.TODO(), &updatedConfigMap, metav1.UpdateOptions{}) - return err -} - -func TestDebugServer(t *testing.T) { - assert := tassert.New(t) - - // set up a controller - stop := make(chan struct{}) - kubeClient, _, cfg, err := setupComponents(testOSMNamespace, testOSMConfigMapName, false, stop) - assert.NoError(err) - - fakeDebugServer := FakeDebugServer{0, 0, nil, false} - httpserver.RegisterDebugServer(&fakeDebugServer, cfg) - - assert.Equal(0, fakeDebugServer.startCount) - assert.Equal(0, fakeDebugServer.stopCount) - - // Start server - err = toggleDebugServer(true, kubeClient) - assert.NoError(err) - - assert.Eventually(func() bool { - return fakeDebugServer.running == true - }, 2*time.Second, 100*time.Millisecond) - assert.Eventually(func() bool { - return fakeDebugServer.startCount == 1 - }, 2*time.Second, 100*time.Millisecond) - assert.Equal(0, fakeDebugServer.stopCount) // No eventually for an non-expected-to-change value - - // Stop it - err = toggleDebugServer(false, kubeClient) - assert.NoError(err) - - assert.Eventually(func() bool { - return fakeDebugServer.running == false - }, 2*time.Second, 100*time.Millisecond) - assert.Eventually(func() bool { - return fakeDebugServer.stopCount == 1 - }, 2*time.Second, 100*time.Millisecond) - assert.Equal(1, fakeDebugServer.startCount) // No eventually for an non-expected-to-change value -} - func TestCreateCABundleKubernetesSecret(t *testing.T) { assert := tassert.New(t) @@ -122,45 +57,3 @@ func TestJoinURL(t *testing.T) { assert.Equal(result, ju.expectedOutput) } } - -type FakeDebugServer struct { - stopCount int - startCount int - stopErr error - running bool -} - -func (f *FakeDebugServer) Stop() error { - f.stopCount++ - if f.stopErr != nil { - return errors.Errorf("Debug server error") - } - f.running = false - return nil -} - -func (f *FakeDebugServer) Start() { - f.startCount++ - f.running = true -} - -func setupComponents(namespace, configMapName string, initialDebugServerEnabled bool, stop chan struct{}) (*testclient.Clientset, v1.ConfigMap, configurator.Configurator, error) { - kubeClient := testclient.NewSimpleClientset() - - defaultConfigMap := map[string]string{ - "enable_debug_server": strconv.FormatBool(initialDebugServerEnabled), - } - configMap := v1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: namespace, - Name: configMapName, - }, - Data: defaultConfigMap, - } - _, err := kubeClient.CoreV1().ConfigMaps(namespace).Create(context.TODO(), &configMap, metav1.CreateOptions{}) - if err != nil { - return kubeClient, configMap, nil, err - } - cfg := configurator.NewConfigurator(kubeClient, stop, namespace, configMapName) - return kubeClient, configMap, cfg, nil -} diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go index d7d6f36045..0f31920879 100644 --- a/pkg/constants/constants.go +++ b/pkg/constants/constants.go @@ -60,8 +60,8 @@ const ( // InjectorWebhookPort is the port on which the sidecar injection webhook listens InjectorWebhookPort = 9090 - // MetricsServerPort is the port on which OSM exposes its own metrics server - MetricsServerPort = 9091 + // OSMServicePort is the port on which OSM exposes HTTP paths for metrics and probes + OSMServicePort = 9091 //DebugPort is the port on which OSM exposes its debug server DebugPort = 9092 diff --git a/pkg/debugger/certificate.go b/pkg/debugger/certificate.go index e8a0a8f2d8..b8125213c6 100644 --- a/pkg/debugger/certificate.go +++ b/pkg/debugger/certificate.go @@ -10,7 +10,7 @@ import ( "github.com/openservicemesh/osm/pkg/certificate" ) -func (ds debugConfig) getCertHandler() http.Handler { +func (ds DebugConfig) getCertHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { certs := ds.certDebugger.ListIssuedCertificates() diff --git a/pkg/debugger/certificate_test.go b/pkg/debugger/certificate_test.go index 38758e28da..e47f9a99c7 100644 --- a/pkg/debugger/certificate_test.go +++ b/pkg/debugger/certificate_test.go @@ -18,7 +18,7 @@ func TestGetCertHandler(t *testing.T) { mockCtrl := gomock.NewController(t) mock := NewMockCertificateManagerDebugger(mockCtrl) - ds := debugConfig{ + ds := DebugConfig{ certDebugger: mock, } diff --git a/pkg/debugger/debugger_config_listener.go b/pkg/debugger/debugger_config_listener.go new file mode 100644 index 0000000000..88c5955439 --- /dev/null +++ b/pkg/debugger/debugger_config_listener.go @@ -0,0 +1,54 @@ +package debugger + +import ( + "github.com/openservicemesh/osm/pkg/announcements" + "github.com/openservicemesh/osm/pkg/constants" + "github.com/openservicemesh/osm/pkg/httpserver" + "github.com/openservicemesh/osm/pkg/kubernetes/events" +) + +// StartDebugServerConfigListener registers a go routine to listen to configuration and configure debug server as needed +func (d *DebugConfig) StartDebugServerConfigListener() { + // Subscribe to configuration updates + ch := events.GetPubSubInstance().Subscribe( + announcements.ConfigMapAdded, + announcements.ConfigMapDeleted, + announcements.ConfigMapUpdated) + + // This is the Debug server + httpDebugServer := httpserver.NewHTTPServer(constants.DebugPort) + httpDebugServer.AddHandlers(d.GetHandlers()) + + // Run config listener + go func(cfgSubChannel chan interface{}, dConf *DebugConfig, httpServ *httpserver.HTTPServer) { + // Bootstrap after subscribing + started := false + + if d.configurator.IsDebugServerEnabled() { + if err := httpDebugServer.Start(); err != nil { + log.Error().Err(err).Msgf("error starting debug server") + } + started = true + } + + for { + <-cfgSubChannel + isDbgSrvEnabled := d.configurator.IsDebugServerEnabled() + + if isDbgSrvEnabled && !started { + if err := httpDebugServer.Start(); err != nil { + log.Error().Err(err).Msgf("error starting debug server") + } else { + started = true + } + } + if !isDbgSrvEnabled && started { + if err := httpDebugServer.Stop(); err != nil { + log.Error().Err(err).Msgf("error stoping debug server") + } else { + started = false + } + } + } + }(ch, d, httpDebugServer) +} diff --git a/pkg/debugger/envoy.go b/pkg/debugger/envoy.go index 9e3733c64d..816daf2d3a 100644 --- a/pkg/debugger/envoy.go +++ b/pkg/debugger/envoy.go @@ -11,7 +11,7 @@ import ( "github.com/openservicemesh/osm/pkg/certificate" ) -func (ds debugConfig) getEnvoyConfig(pod *v1.Pod, cn certificate.CommonName, url string) string { +func (ds DebugConfig) getEnvoyConfig(pod *v1.Pod, cn certificate.CommonName, url string) string { log.Info().Msgf("Getting Envoy config for CN=%s, podIP=%s", cn, pod.Status.PodIP) minPort := 16000 diff --git a/pkg/debugger/feature_flags.go b/pkg/debugger/feature_flags.go index 2ada7b66d6..649c4fed57 100644 --- a/pkg/debugger/feature_flags.go +++ b/pkg/debugger/feature_flags.go @@ -8,7 +8,7 @@ import ( "github.com/openservicemesh/osm/pkg/featureflags" ) -func (ds debugConfig) getFeatureFlags() http.Handler { +func (ds DebugConfig) getFeatureFlags() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if featureFlagsJSON, err := json.Marshal(featureflags.Features); err != nil { log.Error().Err(err).Msgf("Error marshaling feature flags struct: %+v", featureflags.Features) diff --git a/pkg/debugger/index.go b/pkg/debugger/index.go index 12f6103587..fc824116a4 100644 --- a/pkg/debugger/index.go +++ b/pkg/debugger/index.go @@ -5,7 +5,7 @@ import ( "net/http" ) -func (ds debugConfig) getDebugIndex(handlers map[string]http.Handler) http.Handler { +func (ds DebugConfig) getDebugIndex(handlers map[string]http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = fmt.Fprint(w, `