Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions api/tempo/v1alpha1/tempostack_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,28 @@ type TempoStackSpec struct {
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Observability"
Observability ObservabilitySpec `json:"observability,omitempty"`

// NetworkingSpec defines how networking configs are handled.
//
// +optional
// +kubebuilder:validation:Optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Networking"
Networking NetworkingSpec `json:"networking,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should re-work the naming, this seems to ambigous

  spec:
    networking:
      enabled: true

The config does not indicate anything about network policies


// +optional
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Extra Configurations"
ExtraConfig *ExtraConfigSpec `json:"extraConfig,omitempty"`
}

// ObservabilitySpec defines how networking configs are handled.
Copy link
Preview

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment incorrectly states 'ObservabilitySpec' when it should be 'NetworkingSpec'.

Suggested change
// ObservabilitySpec defines how networking configs are handled.
// NetworkingSpec defines how networking configs are handled.

Copilot uses AI. Check for mistakes.

type NetworkingSpec struct {
// Enabled determines whether network policies are generated for the operands.
//
// +optional
// +kubebuilder:default:="true"
// +operator-sdk:csv:customresourcedefinitions:type=spec,displayName="Enable Networkpolicies"
Enabled bool `json:"enabled,omitempty"`
}

// ObservabilitySpec defines how telemetry data gets handled.
type ObservabilitySpec struct {
// Metrics defines the metrics configuration for operands.
Expand Down Expand Up @@ -954,3 +971,4 @@ func (tempo *TempoStack) GetStatus() any {
func (tempo *TempoStack) SetStatus(s any) {
tempo.Status = s.(TempoStackStatus)
}

16 changes: 16 additions & 0 deletions api/tempo/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 @@ -74,7 +74,7 @@ metadata:
capabilities: Deep Insights
categories: Logging & Tracing,Monitoring
containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.17.1
createdAt: "2025-07-23T14:21:28Z"
createdAt: "2025-07-28T12:34:05Z"
description: Create and manage deployments of Tempo, a high-scale distributed
tracing backend.
operatorframework.io/cluster-monitoring: "true"
Expand Down Expand Up @@ -756,6 +756,13 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:select:Managed
- urn:alm:descriptor:com.tectonic.ui:select:Unmanaged
- description: NetworkingSpec defines how networking configs are handled.
displayName: Networking
path: networking
- description: Enabled determines whether network policies are generated for
the operands.
displayName: Enable Networkpolicies
path: networking.enabled
- description: ObservabilitySpec defines how telemetry data gets handled.
displayName: Observability
path: observability
Expand Down
9 changes: 9 additions & 0 deletions bundle/community/manifests/tempo.grafana.com_tempostacks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,15 @@ spec:
- Managed
- Unmanaged
type: string
networking:
description: NetworkingSpec defines how networking configs are handled.
properties:
enabled:
default: "true"
description: Enabled determines whether network policies are generated
for the operands.
type: boolean
type: object
observability:
description: ObservabilitySpec defines how telemetry data gets handled.
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ metadata:
capabilities: Deep Insights
categories: Logging & Tracing,Monitoring
containerImage: ghcr.io/grafana/tempo-operator/tempo-operator:v0.17.1
createdAt: "2025-07-23T14:21:26Z"
createdAt: "2025-07-28T12:33:03Z"
description: Create and manage deployments of Tempo, a high-scale distributed
tracing backend.
operatorframework.io/cluster-monitoring: "true"
Expand Down Expand Up @@ -756,6 +756,13 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:select:Managed
- urn:alm:descriptor:com.tectonic.ui:select:Unmanaged
- description: NetworkingSpec defines how networking configs are handled.
displayName: Networking
path: networking
- description: Enabled determines whether network policies are generated for
the operands.
displayName: Enable Networkpolicies
path: networking.enabled
- description: ObservabilitySpec defines how telemetry data gets handled.
displayName: Observability
path: observability
Expand Down
9 changes: 9 additions & 0 deletions bundle/openshift/manifests/tempo.grafana.com_tempostacks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,15 @@ spec:
- Managed
- Unmanaged
type: string
networking:
description: NetworkingSpec defines how networking configs are handled.
properties:
enabled:
default: "true"
description: Enabled determines whether network policies are generated
for the operands.
type: boolean
type: object
observability:
description: ObservabilitySpec defines how telemetry data gets handled.
properties:
Expand Down
9 changes: 9 additions & 0 deletions config/crd/bases/tempo.grafana.com_tempostacks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,15 @@ spec:
- Managed
- Unmanaged
type: string
networking:
description: NetworkingSpec defines how networking configs are handled.
properties:
enabled:
default: "true"
description: Enabled determines whether network policies are generated
for the operands.
type: boolean
type: object
observability:
description: ObservabilitySpec defines how telemetry data gets handled.
properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,13 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:select:Managed
- urn:alm:descriptor:com.tectonic.ui:select:Unmanaged
- description: NetworkingSpec defines how networking configs are handled.
displayName: Networking
path: networking
- description: Enabled determines whether network policies are generated for
the operands.
displayName: Enable Networkpolicies
path: networking.enabled
- description: ObservabilitySpec defines how telemetry data gets handled.
displayName: Observability
path: observability
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,13 @@ spec:
x-descriptors:
- urn:alm:descriptor:com.tectonic.ui:select:Managed
- urn:alm:descriptor:com.tectonic.ui:select:Unmanaged
- description: NetworkingSpec defines how networking configs are handled.
displayName: Networking
path: networking
- description: Enabled determines whether network policies are generated for
the operands.
displayName: Enable Networkpolicies
path: networking.enabled
- description: ObservabilitySpec defines how telemetry data gets handled.
displayName: Observability
path: observability
Expand Down
2 changes: 2 additions & 0 deletions docs/spec/tempo.grafana.com_tempostacks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ spec: # TempoStackSpec defines the desired st
maxSearchBytesPerTrace: 0 # DEPRECATED. MaxSearchBytesPerTrace defines the maximum size of search data for a single trace in bytes. default: `0` to disable.
maxSearchDuration: "" # MaxSearchDuration defines the maximum allowed time range for a search. If this value is not set, then spec.search.maxDuration is used.
managementState: "Managed" # ManagementState defines if the CR should be managed by the operator or not. Default is managed.
networking: # NetworkingSpec defines how networking configs are handled.
enabled: "true" # Enabled determines whether network policies are generated for the operands.
observability: # ObservabilitySpec defines how telemetry data gets handled.
grafana: # Grafana defines the Grafana configuration for operands.
createDatasource: false # CreateDatasource specifies if a Grafana Datasource should be created for Tempo.
Expand Down
171 changes: 171 additions & 0 deletions internal/manifests/networking/components.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package networking
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a similar comment as the previous one. I would be more specific and rename it to networkpolicies


import (
corev1 "k8s.io/api/core/v1"
networkingv1 "k8s.io/api/networking/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/utils/ptr"

"github.com/grafana/tempo-operator/api/tempo/v1alpha1"
"github.com/grafana/tempo-operator/internal/manifests/manifestutils"
"github.com/grafana/tempo-operator/internal/manifests/naming"
)

func generatePolicyFor(tempo v1alpha1.TempoStack, componentName string) *networkingv1.NetworkPolicy {
np := &networkingv1.NetworkPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: naming.Name(componentName, tempo.Name),
Namespace: tempo.Namespace,
Labels: manifestutils.ComponentLabels(componentName, tempo.Name),
},
Spec: networkingv1.NetworkPolicySpec{
PodSelector: metav1.LabelSelector{
MatchLabels: manifestutils.ComponentLabels(componentName, tempo.Name),
},
},
}

rels := componentRelations(tempo)[componentName]

for target, ports := range rels {
for _, conn := range ports {
peer := policyPeerFor(target, tempo)
np.Spec.Egress = append(np.Spec.Egress, networkingv1.NetworkPolicyEgressRule{
Ports: []networkingv1.NetworkPolicyPort{conn},
To: []networkingv1.NetworkPolicyPeer{peer},
})
}
}

if len(np.Spec.Egress) > 0 {
np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, networkingv1.PolicyTypeEgress)
}

reverse := reverseRelations(componentRelations(tempo))
for source, ports := range reverse {
for target, conn := range ports {
if target != componentName {
continue
}
peer := policyPeerFor(source, tempo)
np.Spec.Ingress = append(np.Spec.Ingress, networkingv1.NetworkPolicyIngressRule{
Ports: conn,
From: []networkingv1.NetworkPolicyPeer{peer},
})
}
}

if len(np.Spec.Ingress) > 0 {
np.Spec.PolicyTypes = append(np.Spec.PolicyTypes, networkingv1.PolicyTypeIngress)
}

return np
}

func policyPeerFor(name string, tempo v1alpha1.TempoStack) networkingv1.NetworkPolicyPeer {
switch name {
case netPolicys3Storage, netPolicyOtelTargets:
return networkingv1.NetworkPolicyPeer{
IPBlock: &networkingv1.IPBlock{CIDR: "0.0.0.0/0"},
}
case netPolicyClusterComponents:
return networkingv1.NetworkPolicyPeer{
NamespaceSelector: &metav1.LabelSelector{},
}
default:
return networkingv1.NetworkPolicyPeer{
PodSelector: &metav1.LabelSelector{
MatchLabels: manifestutils.ComponentLabels(name, tempo.Name),
},
}
}
}

// networkRelations define connections: from -> to using NetworkPolicyPort.
type networkRelations = map[string]map[string][]networkingv1.NetworkPolicyPort

const (
netPolicys3Storage = "s3"
netPolicyOtelTargets = "otel"
netPolicyClusterComponents = "cluster"
)

func componentRelations(tempo v1alpha1.TempoStack) networkRelations {
var (
s3Conn = []networkingv1.NetworkPolicyPort{
{ // TODO: get this from secret?
Protocol: ptr.To(corev1.ProtocolTCP),
Port: ptr.To(intstr.FromInt(443)),
},
{ // TODO: get this from secret?
Protocol: ptr.To(corev1.ProtocolTCP),
Port: ptr.To(intstr.FromInt(9000)),
},
Comment on lines +96 to +104
Copy link
Preview

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded ports 443 and 9000 for S3 connections should be configurable or derived from the storage configuration. The TODO comments indicate this is a known issue that should be addressed.

Copilot uses AI. Check for mistakes.

}
tempoGrpcConn = networkingv1.NetworkPolicyPort{
Protocol: ptr.To(corev1.ProtocolTCP),
Port: ptr.To(intstr.FromString(manifestutils.GrpcPortName)),
}
otelHttpConn = networkingv1.NetworkPolicyPort{
Protocol: ptr.To(corev1.ProtocolTCP),
Port: ptr.To(intstr.FromInt(4318)),
}
otelGrpcConn = networkingv1.NetworkPolicyPort{
Protocol: ptr.To(corev1.ProtocolTCP),
Port: ptr.To(intstr.FromInt(4317)),
}
)
clusterIngress := map[string][]networkingv1.NetworkPolicyPort{}
if tempo.Spec.Template.Gateway.Enabled { // TODO: add cluster -> gateway access
Copy link
Preview

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty port slice assignment should include a comment explaining why no specific ports are defined for gateway access or implement the TODO mentioned in the comment above.

Suggested change
if tempo.Spec.Template.Gateway.Enabled { // TODO: add cluster -> gateway access
if tempo.Spec.Template.Gateway.Enabled { // TODO: add cluster -> gateway access
// No specific ports are defined for gateway access yet; this is intentional and will be updated per the TODO above.

Copilot uses AI. Check for mistakes.

clusterIngress[manifestutils.GatewayComponentName] = []networkingv1.NetworkPolicyPort{}
}

if tempo.Spec.Template.QueryFrontend.JaegerQuery.Enabled { // TODO: add cluster -> jaegerQuery access
Copy link
Preview

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty port slice assignment should include a comment explaining why no specific ports are defined for Jaeger frontend access or implement the TODO mentioned in the comment above.

Suggested change
if tempo.Spec.Template.QueryFrontend.JaegerQuery.Enabled { // TODO: add cluster -> jaegerQuery access
if tempo.Spec.Template.QueryFrontend.JaegerQuery.Enabled { // TODO: add cluster -> jaegerQuery access
// No ports defined for Jaeger frontend access as it is not exposed to the cluster by default.

Copilot uses AI. Check for mistakes.

clusterIngress[manifestutils.JaegerFrontendComponentName] = []networkingv1.NetworkPolicyPort{}
}
return map[string]map[string][]networkingv1.NetworkPolicyPort{
netPolicyClusterComponents: clusterIngress,
manifestutils.DistributorComponentName: {
manifestutils.IngesterComponentName: {
tempoGrpcConn,
},
netPolicyOtelTargets: {
otelGrpcConn,
otelHttpConn,
},
},
manifestutils.IngesterComponentName: {
netPolicys3Storage: s3Conn,
},
manifestutils.QuerierComponentName: {
manifestutils.IngesterComponentName: {
tempoGrpcConn,
},
netPolicys3Storage: s3Conn,
},
manifestutils.QueryFrontendComponentName: {
manifestutils.QuerierComponentName: {
tempoGrpcConn,
},
},
manifestutils.CompactorComponentName: {
netPolicys3Storage: s3Conn,
},
}
}

func reverseRelations(rels map[string]map[string][]networkingv1.NetworkPolicyPort) map[string]map[string][]networkingv1.NetworkPolicyPort {
reverse := map[string]map[string][]networkingv1.NetworkPolicyPort{}

for from, targets := range rels {
for to, ports := range targets {
if _, ok := reverse[to]; !ok {
reverse[to] = map[string][]networkingv1.NetworkPolicyPort{}
}
reverse[to][from] = append(reverse[to][from], ports...)
}
}

return reverse
}
Loading
Loading