-
Notifications
You must be signed in to change notification settings - Fork 196
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test connection for destinations (#1327)
Co-authored-by: Amir Blum <amirgiraffe@gmail.com>
- Loading branch information
Showing
16 changed files
with
623 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,3 +27,4 @@ spec: | |
type: password | ||
required: true | ||
secret: true | ||
testConnectionSupported: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,3 +20,4 @@ spec: | |
componentProps: | ||
type: text | ||
required: true | ||
testConnectionSupported: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,3 +29,4 @@ spec: | |
- https://otlp.nr-data.net | ||
- https://otlp.eu01.nr-data.net | ||
required: true | ||
testConnectionSupported: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
frontend/endpoints/test_connection/otlp_test_connection.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package testconnection | ||
|
||
import ( | ||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/exporter" | ||
|
||
"go.opentelemetry.io/collector/exporter/otlpexporter" | ||
) | ||
|
||
var _ ExporterConnectionTester = &otlpExporterConnectionTester{} | ||
|
||
type otlpExporterConnectionTester struct { | ||
f exporter.Factory | ||
} | ||
|
||
func NewOTLPTester() *otlpExporterConnectionTester { | ||
return &otlpExporterConnectionTester{ | ||
f: otlpexporter.NewFactory(), | ||
} | ||
} | ||
|
||
func (t *otlpExporterConnectionTester) Factory() exporter.Factory { | ||
return t.f | ||
} | ||
|
||
func (t *otlpExporterConnectionTester) ModifyConfigForConnectionTest(cfg component.Config) component.Config { | ||
otlpConf, ok := cfg.(*otlpexporter.Config) | ||
if !ok { | ||
return nil | ||
} | ||
|
||
// currently using the default timeout config of the collector - 5 seconds | ||
// Avoid batching and retries | ||
otlpConf.QueueConfig.Enabled = false | ||
otlpConf.RetryConfig.Enabled = false | ||
return otlpConf | ||
} |
36 changes: 36 additions & 0 deletions
36
frontend/endpoints/test_connection/otlphttp_test_connection.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package testconnection | ||
|
||
import ( | ||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/exporter" | ||
"go.opentelemetry.io/collector/exporter/otlphttpexporter" | ||
) | ||
|
||
var _ ExporterConnectionTester = &otlphttpExporterConnectionTester{} | ||
|
||
type otlphttpExporterConnectionTester struct { | ||
f exporter.Factory | ||
} | ||
|
||
func NewOTLPHTTPTester() *otlphttpExporterConnectionTester { | ||
return &otlphttpExporterConnectionTester{ | ||
f: otlphttpexporter.NewFactory(), | ||
} | ||
} | ||
|
||
func (t *otlphttpExporterConnectionTester) Factory() exporter.Factory { | ||
return t.f | ||
} | ||
|
||
func (t *otlphttpExporterConnectionTester) ModifyConfigForConnectionTest(cfg component.Config) component.Config { | ||
otlpConf, ok := cfg.(*otlphttpexporter.Config) | ||
if !ok { | ||
return nil | ||
} | ||
|
||
// currently using the default timeout config of the collector - 5 seconds | ||
// Avoid batching and retries | ||
otlpConf.QueueConfig.Enabled = false | ||
otlpConf.RetryConfig.Enabled = false | ||
return otlpConf | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package testconnection | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
|
||
"github.com/odigos-io/odigos/common" | ||
"github.com/odigos-io/odigos/common/config" | ||
|
||
"go.opentelemetry.io/collector/component" | ||
"go.opentelemetry.io/collector/confmap" | ||
"go.opentelemetry.io/collector/exporter" | ||
"go.opentelemetry.io/collector/exporter/exportertest" | ||
"go.opentelemetry.io/collector/pdata/ptrace" | ||
) | ||
|
||
var ( | ||
configres map[common.DestinationType]config.Configer | ||
connectionTesters = []ExporterConnectionTester{ | ||
NewOTLPTester(), // "otlp/" prefix | ||
NewOTLPHTTPTester(), // "otlphttp/" prefix | ||
} | ||
) | ||
|
||
func init() { | ||
var err error | ||
configres, err = config.LoadConfigers() | ||
if err != nil { | ||
panic(1) | ||
} | ||
} | ||
|
||
type TestConnectionErrorReason string | ||
|
||
const ( | ||
UnKnownDestination TestConnectionErrorReason = "unknown destination" | ||
InvalidConfig TestConnectionErrorReason = "invalid config" | ||
UnsupportedExporterType TestConnectionErrorReason = "unsupported exporter type" | ||
FailedToConnect TestConnectionErrorReason = "failed to connect" | ||
) | ||
|
||
type TestConnectionResult struct { | ||
Succeeded bool | ||
Message string | ||
Reason TestConnectionErrorReason | ||
StatusCode int | ||
DestinationType common.DestinationType | ||
} | ||
|
||
type ExporterConnectionTester interface { | ||
// Factory returns the exporter factory for the exporter type. | ||
// This is used to create the exporter instance for testing the connection. | ||
Factory() exporter.Factory | ||
// ModifyConfigForConnectionTest modifies the exporter configuration for testing the connection. | ||
// Since the default configuration may have batching, retries, etc. which may not be suitable for testing the connection. | ||
ModifyConfigForConnectionTest(component.Config) component.Config | ||
} | ||
|
||
func getConnectionTester(exporterID string) ExporterConnectionTester { | ||
for _, tester := range connectionTesters { | ||
prefix := fmt.Sprintf("%s/", tester.Factory().Type().String()) | ||
if strings.HasPrefix(exporterID, prefix) { | ||
return tester | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func TestConnection(ctx context.Context, dest config.ExporterConfigurer) TestConnectionResult { | ||
destType := dest.GetType() | ||
configer, ok := configres[destType] | ||
if !ok { | ||
return TestConnectionResult{Succeeded: false, Reason: UnKnownDestination, DestinationType: destType, StatusCode: http.StatusNotImplemented} | ||
} | ||
|
||
currentConfig := config.Config{ | ||
Exporters: make(config.GenericMap), | ||
Service: config.Service{ | ||
Pipelines: make(map[string]config.Pipeline), | ||
}, | ||
} | ||
err := configer.ModifyConfig(dest, ¤tConfig) | ||
if err != nil { | ||
return TestConnectionResult{Succeeded: false, Message: err.Error(), Reason: InvalidConfig, DestinationType: destType, StatusCode: http.StatusInternalServerError} | ||
} | ||
|
||
exporters := currentConfig.Exporters | ||
if len(exporters) == 0 { | ||
return TestConnectionResult{Message: "no exporters found in config", Reason: InvalidConfig, DestinationType: destType, StatusCode: http.StatusInternalServerError, Succeeded: false} | ||
} | ||
|
||
var exporterRawConfig config.GenericMap | ||
var connectionTester ExporterConnectionTester | ||
foundTester := false | ||
for componentID, cfg := range exporters { | ||
gm, ok := cfg.(config.GenericMap) | ||
if !ok { | ||
continue | ||
} | ||
ct := getConnectionTester(componentID) | ||
if ct != nil { | ||
connectionTester = ct | ||
foundTester = true | ||
exporterRawConfig = gm | ||
break | ||
} | ||
} | ||
|
||
if !foundTester { | ||
return TestConnectionResult{Succeeded: false, Message: "no supported exporter found in config", Reason: UnsupportedExporterType, DestinationType: destType, StatusCode: http.StatusNotFound} | ||
} | ||
|
||
// before testing the connection, replace placeholders (if exists) in the config with actual values | ||
replacePlaceholders(exporterRawConfig, dest.GetConfig()) | ||
defaultConfig := connectionTester.Factory().CreateDefaultConfig() | ||
connectionTester.ModifyConfigForConnectionTest(defaultConfig) | ||
|
||
// convert the user provided fields to a collector config | ||
exportersConf := confmap.NewFromStringMap(exporterRawConfig) | ||
if exportersConf == nil { | ||
return TestConnectionResult{Succeeded: false, Message: "failed to create exporter config", Reason: InvalidConfig, DestinationType: destType, StatusCode: http.StatusInternalServerError} | ||
} | ||
|
||
// unmarshal the user provided configuration into the default one, merging them | ||
err = exportersConf.Unmarshal(&defaultConfig) | ||
if err != nil { | ||
return TestConnectionResult{Succeeded: false, Message: err.Error(), Reason: InvalidConfig, DestinationType: destType, StatusCode: http.StatusInternalServerError} | ||
} | ||
|
||
if validator, ok := defaultConfig.(component.ConfigValidator); ok { | ||
// if the component has a Validate method, call it to validate the configuration | ||
err = validator.Validate() | ||
if err != nil { | ||
return TestConnectionResult{Succeeded: false, Message: err.Error(), Reason: InvalidConfig, DestinationType: destType, StatusCode: http.StatusInternalServerError} | ||
} | ||
} | ||
|
||
exporter, err := connectionTester.Factory().CreateTracesExporter(ctx, exportertest.NewNopCreateSettings(), defaultConfig) | ||
if err != nil { | ||
return TestConnectionResult{Succeeded: false, Message: err.Error(), Reason: InvalidConfig, DestinationType: destType, StatusCode: http.StatusInternalServerError} | ||
} | ||
|
||
err = exporter.Start(ctx, nil) | ||
if err != nil { | ||
return TestConnectionResult{Succeeded: false, Message: err.Error(), Reason: FailedToConnect, DestinationType: destType, StatusCode: http.StatusInternalServerError} | ||
} | ||
|
||
defer exporter.Shutdown(ctx) | ||
err = exporter.ConsumeTraces(ctx, ptrace.NewTraces()) | ||
if err != nil { | ||
return TestConnectionResult{Succeeded: false, Message: err.Error(), Reason: FailedToConnect, DestinationType: destType, StatusCode: http.StatusInternalServerError} | ||
} | ||
|
||
return TestConnectionResult{Succeeded: true, DestinationType: destType, StatusCode: http.StatusOK} | ||
} |
Oops, something went wrong.