Skip to content

Commit

Permalink
Add env vars to configure OpenTelemetry metrics/tracing
Browse files Browse the repository at this point in the history
  • Loading branch information
christinaexyou committed Feb 6, 2025
1 parent 9b66494 commit 6a188c0
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 0 deletions.
29 changes: 29 additions & 0 deletions api/gorch/v1alpha1/guardrailsorchestrator_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,37 @@ type GuardrailsOrchestratorSpec struct {
Replicas int32 `json:"replicas"`
// Name of configmap containing generator,detector,and chunker arguments
OrchestratorConfig *string `json:"orchestratorConfig"`
// Name of the configmap containg vLLM gateway arguments
// +optional
VLLMGatewayConfig *string `json:"vllmGatewayConfig,omitempty"`
// List of orchestrator enviroment variables for configuring the OTLP exporter
// +optional
OtelExporter OtelExporter `json:"otelExporter,omitempty"`
}

// OtelExporter defines the environment variables for configuring the OTLP exporter.
type OtelExporter struct {
// Sets the protocol for all the OTLP endpoints
// +optional
Protocol string `json:"protocol,omitempty"`
// Overrides the protocol for traces
// +optional
TracesProtocol string `json:"tracesProtocol,omitempty"`
// Overrides the protocol for traces
// +optional
MetricsProtocol string `json:"metricsProtocol,omitempty"`
// Sets the OTLP endpoint
// +optional
OTLPEndpoint string `json:"otlpEndpoint,omitempty"`
// Overrides the OTLP endpoint for metrics
// +optional
MetricsEndpoint string `json:"metricsEndpoint,omitempty"`
// Overrides the OTLP endpoint for traces
// +optional
TracesEndpoint string `json:"tracesEndpoint,omitempty"`
// Specifies which data types to export
// +optional
OTLPExport string `json:"otlpExport,omitempty"`
}

type ConditionType string
Expand Down
16 changes: 16 additions & 0 deletions api/gorch/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,38 @@ spec:
description: Name of configmap containing generator,detector,and chunker
arguments
type: string
otelExporter:
description: List of orchestrator enviroment variables for configuring
the OTLP exporter
properties:
metricsEndpoint:
description: Overrides the OTLP endpoint for metrics
type: string
metricsProtocol:
description: Overrides the protocol for traces
type: string
otlpEndpoint:
description: Sets the OTLP endpoint
type: string
otlpExport:
description: Specifies which data types to export
type: string
protocol:
description: Sets the protocol for all the OTLP endpoints
type: string
tracesEndpoint:
description: Overrides the OTLP endpoint for traces
type: string
tracesProtocol:
description: Overrides the protocol for traces
type: string
type: object
replicas:
description: Number of replicas
format: int32
type: integer
vllmGatewayConfig:
description: ' Name of the configmap containg vLLM gateway arguments'
type: string
required:
- orchestratorConfig
Expand Down
150 changes: 150 additions & 0 deletions controllers/gorch/guardrailsorchestrator_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,31 @@ func createGuardrailsOrchestratorSidecar(ctx context.Context, orchestratorConfig
return err
}

func createGuardrailsOrchestratorOtelExporter(ctx context.Context, orchestratorConfigMap string) error {
typedNamespacedName := types.NamespacedName{Name: orchestratorName, Namespace: namespaceName}
otelExporter := gorchv1alpha1.OtelExporter{
Protocol: "grpc",
OTLPEndpoint: "localhost:4317",
OTLPExport: "traces",
}
err := k8sClient.Get(ctx, typedNamespacedName, &gorchv1alpha1.GuardrailsOrchestrator{})
if err != nil && errors.IsNotFound(err) {
gorch := &gorchv1alpha1.GuardrailsOrchestrator{
ObjectMeta: metav1.ObjectMeta{
Name: typedNamespacedName.Name,
Namespace: typedNamespacedName.Namespace,
},
Spec: gorchv1alpha1.GuardrailsOrchestratorSpec{
Replicas: 1,
OrchestratorConfig: &orchestratorConfigMap,
OtelExporter: otelExporter,
},
}
err = k8sClient.Create(ctx, gorch)
}
return err
}

func deleteGuardrailsOrchestrator(ctx context.Context, namespace string) error {
typedNamespacedName := types.NamespacedName{Name: orchestratorName, Namespace: namespace}
err := k8sClient.Get(ctx, typedNamespacedName, &gorchv1alpha1.GuardrailsOrchestrator{})
Expand Down Expand Up @@ -336,9 +361,134 @@ func testCreateDeleteGuardrailsOrchestratorSidecar(namespaceName string) {
})
}

func testCreateDeleteGuardrailsOrchestratorOtelExporter(namespaceName string) {
It("Should sucessfully reconcile creating a custom resource for the GuardrailsOrchestrator", func() {
By("Creating an Orchestrator configmap")
configMap := &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: orchestratorName + "-config",
Namespace: namespaceName,
},
}
err := k8sClient.Create(ctx, configMap)
Expect(err).ToNot(HaveOccurred())

By("Creating a custom resource for the GuardrailsOrchestrator")
ctx := context.Background()
typedNamespacedName := types.NamespacedName{Name: orchestratorName, Namespace: namespaceName}
err = createGuardrailsOrchestratorOtelExporter(ctx, configMap.Name)
Expect(err).ToNot(HaveOccurred())

By("Checking if the custom resource was successfully created")
err = k8sClient.Get(ctx, typedNamespacedName, &gorchv1alpha1.GuardrailsOrchestrator{})
Expect(err).ToNot(HaveOccurred())

By("Creating the TrustyAI configmap with sidecar images")
configMap = &corev1.ConfigMap{
TypeMeta: metav1.TypeMeta{
Kind: "ConfigMap",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: constants.ConfigMap,
Namespace: namespaceName,
},
Data: map[string]string{
orchestratorImageKey: "quay.io/trustyai/ta-guardrails-orchestrator:latest",
},
}
err = k8sClient.Create(ctx, configMap)
Expect(err).ToNot(HaveOccurred())

By("Reconciling the custom resource that was created")
reconciler := &GuardrailsOrchestratorReconciler{
Client: k8sClient,
Scheme: k8sClient.Scheme(),
}

_, err = reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: typedNamespacedName})
Expect(err).ToNot(HaveOccurred())

By("Checking if resources were successfully created in the reconcilation")
Eventually(func() error {
configMap := &corev1.ConfigMap{}
if err := k8sClient.Get(ctx, types.NamespacedName{Name: constants.ConfigMap, Namespace: namespaceName}, configMap); err != nil {
return err
}
Expect(configMap.Namespace).Should(Equal(namespaceName))
Expect(configMap.Name).Should(Equal(constants.ConfigMap))

serviceAccount := &corev1.ServiceAccount{}
if err := k8sClient.Get(ctx, types.NamespacedName{Name: orchestratorName + "-serviceaccount", Namespace: namespaceName}, serviceAccount); err != nil {
return err
}

deployment := &appsv1.Deployment{}
if err = k8sClient.Get(ctx, types.NamespacedName{Name: orchestratorName, Namespace: namespaceName}, deployment); err != nil {
return err
}
var container *corev1.Container
var envVar *corev1.EnvVar
Expect(*deployment.Spec.Replicas).Should(Equal(int32(1)))
Expect(deployment.Namespace).Should(Equal(namespaceName))
Expect(deployment.Name).Should(Equal(orchestratorName))
Expect(deployment.Labels["app"]).Should(Equal(orchestratorName))
Expect(deployment.Spec.Template.Spec.Volumes[0].Name).Should(Equal(orchestratorName + "-config"))
container = getContainers(orchestratorName, deployment.Spec.Template.Spec.Containers)
Expect(container.Image).Should(Equal("quay.io/trustyai/ta-guardrails-orchestrator:latest"))
Expect(container.VolumeMounts[0].Name).Should(Equal(orchestratorName + "-config"))
envVar = getEnvVar("OTEL_EXPORTER_OTLP_PROTOCOL", container.Env)
Expect(envVar).ShouldNot(BeNil())
Expect(envVar.Value).To(Equal("grpc"))
envVar = getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT", container.Env)
Expect(envVar).ShouldNot(BeNil())
Expect(envVar.Value).To(Equal("localhost:4317"))
envVar = getEnvVar("OTLP_EXPORT", container.Env)
Expect(envVar).ShouldNot(BeNil())
Expect(envVar.Value).To(Equal("traces"))

service := &corev1.Service{}
if err := k8sClient.Get(ctx, types.NamespacedName{Name: orchestratorName + "-service", Namespace: namespaceName}, service); err != nil {
return err
}
Expect(service.Namespace).Should(Equal(namespaceName))

route := &routev1.Route{}
if err := routev1.AddToScheme(scheme.Scheme); err != nil {
return err
}
if err := k8sClient.Get(ctx, types.NamespacedName{Name: orchestratorName + "-route", Namespace: namespaceName}, route); err != nil {
return err
}
return nil
}, time.Second*10, time.Millisecond*10).Should(Succeed())

By("Deleting the custom resource for the GuardrailsOrchestrator")
err = deleteGuardrailsOrchestrator(ctx, namespaceName)
Expect(err).ToNot(HaveOccurred())

By("Deleting the orchestrator configmap")
err = k8sClient.Delete(ctx, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: orchestratorName + "-config", Namespace: namespaceName}})
Expect(err).ToNot(HaveOccurred())

By("Deleting the TrustyAI configmap")
err = k8sClient.Delete(ctx, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: constants.ConfigMap, Namespace: namespaceName}})
Expect(err).ToNot(HaveOccurred())

By("Reconciling the custom resource that was deleted")
_, err = reconciler.Reconcile(ctx, reconcile.Request{NamespacedName: typedNamespacedName})
Expect(err).ToNot(HaveOccurred())
})
}

var _ = Describe("GuardrailsOrchestrator Controller", func() {
Context("GuardrailsOrchestrator Controller Test", func() {
testCreateDeleteGuardrailsOrchestrator(namespaceName)
testCreateDeleteGuardrailsOrchestratorSidecar(namespaceName)
testCreateDeleteGuardrailsOrchestratorOtelExporter(namespaceName)
})
})
17 changes: 17 additions & 0 deletions controllers/gorch/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,23 @@ var cancel context.CancelFunc

const namespaceName = "test"

func getContainers(containerName string, containers []corev1.Container) *corev1.Container {
for _, container := range containers {
if container.Name == containerName {
return &container
}
}
return nil
}
func getEnvVar(envVarName string, envVars []corev1.EnvVar) *corev1.EnvVar {
for _, envVar := range envVars {
if envVar.Name == envVarName {
return &envVar
}
}
return nil
}

func TestControllers(t *testing.T) {
RegisterFailHandler(Fail)

Expand Down
28 changes: 28 additions & 0 deletions controllers/gorch/templates/deployment.tmpl.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,34 @@ spec:
value: 'full'
- name: RUST_LOG
value: 'info'
{{if .Orchestrator.Spec.OtelExporter.Protocol}}
- name: OTEL_EXPORTER_OTLP_PROTOCOL
value: {{.Orchestrator.Spec.OtelExporter.Protocol}}
{{end}}
{{if .Orchestrator.Spec.OtelExporter.TracesProtocol}}
- name: OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
value: {{.Orchestrator.Spec.OtelExporter.TracesProtocol}}
{{end}}
{{if .Orchestrator.Spec.OtelExporter.MetricsProtocol}}
- name: OTEL_EXPORTER_OTLP_METRICS_PROTOCOL
value: {{.Orchestrator.Spec.OtelExporter.MetricsProtocol}}
{{end}}
{{if .Orchestrator.Spec.OtelExporter.OTLPEndpoint}}
- name: OTEL_EXPORTER_OTLP_ENDPOINT
value: {{.Orchestrator.Spec.OtelExporter.OTLPEndpoint}}
{{end}}
{{if .Orchestrator.Spec.OtelExporter.TracesEndpoint}}
- name: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
value: {{.Orchestrator.Spec.OtelExporter.TracesEndpoint}}
{{end}}
{{if .Orchestrator.Spec.OtelExporter.MetricsEndpoint}}
- name: OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
value: {{.Orchestrator.Spec.OtelExporter.MetricsEndpoint}}
{{end}}
{{if .Orchestrator.Spec.OtelExporter.OTLPExport}}
- name: OTLP_EXPORT
value: {{.Orchestrator.Spec.OtelExporter.OTLPExport}}
{{end}}
volumeMounts:
- name: {{.Orchestrator.Name}}-config
readOnly: true
Expand Down

0 comments on commit 6a188c0

Please sign in to comment.