diff --git a/README.md b/README.md index 383bcbbfc..19de733bc 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ For more details, see our [quickstart guide](https://docs.odigos.io/intro). | Google Cloud Monitoring | ✅ | ✅ | | | Google Cloud Storage | ✅ | | ✅ | | Grafana Cloud | ✅ | ✅ | ✅ | +| Groundcover inCloud | ✅ | ✅ | ✅ | | Honeycomb | ✅ | ✅ | ✅ | | Last9 | ✅ | ✅ | | | Lightstep | ✅ | | | diff --git a/common/config/groundcover.go b/common/config/groundcover.go new file mode 100644 index 000000000..24941fbca --- /dev/null +++ b/common/config/groundcover.go @@ -0,0 +1,71 @@ +package config + +import ( + "errors" + + "github.com/odigos-io/odigos/common" +) + +const ( + GroundcoverEndpoint = "GROUNDCOVER_ENDPOINT" + GroundcoverApiKey = "GROUNDCOVER_API_KEY" +) + +var ( + ErrorGroundcoverEndpointMissing = errors.New("Groundcover is missing a required field (\"GROUNDCOVER_ENDPOINT\"), Groundcover will not be configured") + ErrorGroundcoverApiKeyMissing = errors.New("Groundcover is missing a required field (\"GROUNDCOVER_API_KEY\"), Groundcover will not be configured") +) + +type Groundcover struct{} + +func (j *Groundcover) DestType() common.DestinationType { + return common.GroundcoverDestinationType +} + +func (j *Groundcover) ModifyConfig(dest ExporterConfigurer, currentConfig *Config) error { + config := dest.GetConfig() + uniqueUri := "groundcover-" + dest.GetID() + + url, exists := config[GroundcoverEndpoint] + if !exists { + return ErrorGroundcoverEndpointMissing + } + + endpoint, err := parseOtlpGrpcUrl(url, true) + if err != nil { + return err + } + + exporterName := "otlp/" + uniqueUri + exporterConfig := GenericMap{ + "endpoint": endpoint, + "headers": GenericMap{ + "apikey": "${GROUNDCOVER_API_KEY}", + }, + } + + currentConfig.Exporters[exporterName] = exporterConfig + + if isTracingEnabled(dest) { + tracesPipelineName := "traces/" + uniqueUri + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isMetricsEnabled(dest) { + tracesPipelineName := "metrics/" + uniqueUri + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + if isLoggingEnabled(dest) { + tracesPipelineName := "logs/" + uniqueUri + currentConfig.Service.Pipelines[tracesPipelineName] = Pipeline{ + Exporters: []string{exporterName}, + } + } + + return nil +} diff --git a/common/config/root.go b/common/config/root.go index eb5bd9db4..a03518485 100644 --- a/common/config/root.go +++ b/common/config/root.go @@ -17,7 +17,7 @@ const ( var availableConfigers = []Configer{ &AppDynamics{}, &Axiom{}, &AWSS3{}, &AzureBlobStorage{}, &Causely{}, &Chronosphere{}, &Clickhouse{}, &Coralogix{}, &Datadog{}, &Debug{}, &Dynatrace{}, &ElasticAPM{}, &Elasticsearch{}, &GenericOTLP{}, &GoogleCloud{}, - &GoogleCloudStorage{}, &GrafanaCloudLoki{}, &GrafanaCloudPrometheus{}, &GrafanaCloudTempo{}, + &GoogleCloudStorage{}, &GrafanaCloudLoki{}, &GrafanaCloudPrometheus{}, &GrafanaCloudTempo{}, &Groundcover{}, &Honeycomb{}, &Jaeger{}, &Last9{}, &Lightstep{}, &Logzio{}, &Loki{}, &Lumigo{}, &Middleware{}, &Mock{}, &NewRelic{}, &Nop{}, &OpsVerse{}, &OTLPHttp{}, &Prometheus{}, &Qryn{}, &QrynOSS{}, &Quickwit{}, &Sentry{}, &Signoz{}, &Splunk{}, &SumoLogic{}, &Tempo{}, &Uptrace{}, diff --git a/common/dests.go b/common/dests.go index 1da98cc5a..5f6b4803b 100644 --- a/common/dests.go +++ b/common/dests.go @@ -22,6 +22,7 @@ const ( GrafanaCloudLokiDestinationType DestinationType = "grafanacloudloki" GrafanaCloudPrometheusDestinationType DestinationType = "grafanacloudprometheus" GrafanaCloudTempoDestinationType DestinationType = "grafanacloudtempo" + GroundcoverDestinationType DestinationType = "groundcover" HoneycombDestinationType DestinationType = "honeycomb" JaegerDestinationType DestinationType = "jaeger" Last9DestinationType DestinationType = "last9" diff --git a/destinations/data/groundcover.yaml b/destinations/data/groundcover.yaml new file mode 100644 index 000000000..8a0261e1b --- /dev/null +++ b/destinations/data/groundcover.yaml @@ -0,0 +1,30 @@ +apiVersion: internal.odigos.io/v1beta1 +kind: Destination +metadata: + type: groundcover + displayName: Groundcover inCloud + category: managed +spec: + image: groundcover.svg + signals: + traces: + supported: true + metrics: + supported: true + logs: + supported: true + fields: + - name: GROUNDCOVER_ENDPOINT + displayName: Groundcover inCloud Site + componentType: input + componentProps: + type: text + required: true + tooltip: 'Your inCloud Site is part of the configuration provided to you by groundcover when setting up the managed inCloud backend.' + - name: GROUNDCOVER_API_KEY + displayName: Groundcover API Key + componentType: input + secret: true + componentProps: + type: password + required: true diff --git a/destinations/logos/groundcover.svg b/destinations/logos/groundcover.svg new file mode 100644 index 000000000..9790b8072 --- /dev/null +++ b/destinations/logos/groundcover.svg @@ -0,0 +1,6 @@ + + Groundcover + + + + \ No newline at end of file diff --git a/docs/backends-overview.mdx b/docs/backends-overview.mdx index f13200f5a..24a008b3f 100644 --- a/docs/backends-overview.mdx +++ b/docs/backends-overview.mdx @@ -21,6 +21,7 @@ Odigos has destinations for many observability backends. | Google Cloud Monitoring | Managed | ✅ | ✅ | | | Google Cloud Storage | Managed | ✅ | | ✅ | | Grafana Cloud | Managed | ✅ | ✅ | ✅ | +| Groundcover inCloud | Managed | ✅ | ✅ | ✅ | | Honeycomb | Managed | ✅ | ✅ | ✅ | | Jaeger | Self-Hosted | ✅ | | | | Last9 | Managed | ✅ | ✅ | | diff --git a/docs/backends/groundcover.mdx b/docs/backends/groundcover.mdx new file mode 100644 index 000000000..795b83d8c --- /dev/null +++ b/docs/backends/groundcover.mdx @@ -0,0 +1,60 @@ +--- +title: 'Groundcover inCloud' +--- + +## Configuring Backend + +- **GROUNDCOVER_ENDPOINT** - Endpoint, the format is `host:port`. + - `host` is required, also known as your `inCloud_Site`, it is part of the configuration provided to you by Groundcover when setting up the [inCloud Managed](https://docs.groundcover.com/architecture/incloud-managed) backend. + - `port` is optional, and defaults to the default OpenTelemetry gRPC port `4317`. +- **GROUNDCOVER_API_KEY** - API Key provided by Groundcover, refer to [these docs](https://docs.groundcover.com/architecture/incloud-managed/ingestion-endpoints#fetching-the-api-key) for more info. + +## Adding a Destination to Odigos + +Odigos makes it simple to add and configure destinations, allowing you to select the specific signals [traces/logs/metrics] that you want to send to each destination. There are two primary methods for configuring destinations in Odigos: + +1. **Using the UI** + +Use the [Odigos CLI](https://docs.odigos.io/cli/odigos_ui) to access the UI: + +```bash +odigos ui +``` + +2. **Using kubernetes manifests** + +Save the YAML below to a file (e.g., `destination.yaml`) and apply it using `kubectl`: + +```bash +kubectl apply -f destination.yaml +``` + + +```yaml +apiVersion: odigos.io/v1alpha1 +kind: Destination +metadata: + name: groundcover-example + namespace: odigos-system +spec: + data: + GROUNDCOVER_ENDPOINT: + destinationName: groundcover + secretRef: + name: groundcover-secret + signals: + - TRACES + - METRICS + - LOGS + type: groundcover + +--- +apiVersion: v1 +data: + GROUNDCOVER_API_KEY: +kind: Secret +metadata: + name: groundcover-secret + namespace: odigos-system +type: Opaque +``` diff --git a/docs/mint.json b/docs/mint.json index 425618551..8de7f23a4 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -195,6 +195,7 @@ "backends/grafanacloudloki", "backends/grafanacloudprometheus", "backends/grafanacloudtempo", + "backends/groundcover", "backends/honeycomb", "backends/jaeger", "backends/last9", diff --git a/docs/quickstart/next-steps.mdx b/docs/quickstart/next-steps.mdx index 8c2f5cc17..e863e5d99 100644 --- a/docs/quickstart/next-steps.mdx +++ b/docs/quickstart/next-steps.mdx @@ -33,6 +33,7 @@ Select the relevant backend for your use case below to connect it to Odigos. +