mirror of
https://github.com/grafana/loki.git
synced 2026-03-13 09:33:58 +08:00
fix(operator): dynamically configure networkpolicy when a Kubernetes service is used for object storage (#20111)
Co-authored-by: Robert Jacob <rojacob@redhat.com>
This commit is contained in:
@@ -152,7 +152,7 @@ metadata:
|
||||
categories: OpenShift Optional, Logging & Tracing
|
||||
certified: "false"
|
||||
containerImage: docker.io/grafana/loki-operator:0.9.0
|
||||
createdAt: "2026-02-09T14:56:06Z"
|
||||
createdAt: "2026-02-16T17:31:28Z"
|
||||
description: The Community Loki Operator provides Kubernetes native deployment
|
||||
and management of Loki and related logging components.
|
||||
features.operators.openshift.io/disconnected: "true"
|
||||
@@ -1746,6 +1746,14 @@ spec:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- discovery.k8s.io
|
||||
resources:
|
||||
- endpointslices
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- loki.grafana.com
|
||||
resources:
|
||||
|
||||
@@ -152,7 +152,7 @@ metadata:
|
||||
categories: OpenShift Optional, Logging & Tracing
|
||||
certified: "false"
|
||||
containerImage: docker.io/grafana/loki-operator:0.9.0
|
||||
createdAt: "2026-02-09T14:56:04Z"
|
||||
createdAt: "2026-02-16T17:31:26Z"
|
||||
description: The Community Loki Operator provides Kubernetes native deployment
|
||||
and management of Loki and related logging components.
|
||||
operators.operatorframework.io/builder: operator-sdk-unknown
|
||||
@@ -1726,6 +1726,14 @@ spec:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- discovery.k8s.io
|
||||
resources:
|
||||
- endpointslices
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- loki.grafana.com
|
||||
resources:
|
||||
|
||||
@@ -152,7 +152,7 @@ metadata:
|
||||
categories: OpenShift Optional, Logging & Tracing
|
||||
certified: "false"
|
||||
containerImage: quay.io/openshift-logging/loki-operator:0.1.0
|
||||
createdAt: "2026-02-09T14:56:07Z"
|
||||
createdAt: "2026-02-16T17:31:30Z"
|
||||
description: |
|
||||
The Loki Operator for OCP provides a means for configuring and managing a Loki stack for cluster logging.
|
||||
## Prerequisites and Requirements
|
||||
@@ -1731,6 +1731,14 @@ spec:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- discovery.k8s.io
|
||||
resources:
|
||||
- endpointslices
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- loki.grafana.com
|
||||
resources:
|
||||
|
||||
@@ -77,6 +77,14 @@ rules:
|
||||
- create
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- discovery.k8s.io
|
||||
resources:
|
||||
- endpointslices
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- loki.grafana.com
|
||||
resources:
|
||||
|
||||
51
operator/hack/deploy-odf-storage-secret.sh
Executable file
51
operator/hack/deploy-odf-storage-secret.sh
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# usage: deploy-odf-storage-secret.sh <bucket-claim-name>
|
||||
#
|
||||
# This scripts deploys a LokiStack Secret resource holding the
|
||||
# authentication credentials to access ODF S3 bucket provided by the bucket
|
||||
# claim name.
|
||||
#
|
||||
# bucket_claim_name is the name of the bucket claim to be used by this script to
|
||||
# fetch the credentials to access the bucket.
|
||||
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
readonly bucket_claim_name=${1-}
|
||||
|
||||
if [[ -z "${bucket_claim_name}" ]]; then
|
||||
echo "Provide a bucket claim name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
readonly namespace=${NAMESPACE:-openshift-logging}
|
||||
|
||||
# static authentication from the current select AWS CLI profile.
|
||||
bucket_name=${BUCKET_NAME:-$(oc -n "${namespace}" get configmap "${bucket_claim_name}" -o json | jq -r '.data.BUCKET_NAME')}
|
||||
readonly bucket_name
|
||||
access_key_id=${ACCESS_KEY_ID:-$(oc -n "${namespace}" get secret "${bucket_claim_name}" -o json | jq -r '.data.AWS_ACCESS_KEY_ID' | base64 -d)}
|
||||
readonly access_key_id
|
||||
secret_access_key=${SECRET_ACCESS_KEY:-$( oc -n "${namespace}" get secret "${bucket_claim_name}" -o json | jq -r '.data.AWS_SECRET_ACCESS_KEY' | base64 -d)}
|
||||
readonly secret_access_key
|
||||
|
||||
|
||||
create_secret_args=( \
|
||||
--from-literal=bucketnames="$(echo -n "${bucket_name}")" \
|
||||
--from-literal=access_key_id="$(echo -n "${access_key_id}")" \
|
||||
--from-literal=access_key_secret="$(echo -n "${secret_access_key}")" \
|
||||
--from-literal=endpoint="$(echo -n "https://s3.openshift-storage.svc")" \
|
||||
)
|
||||
|
||||
kubectl --ignore-not-found=true -n "${namespace}" delete secret test
|
||||
kubectl -n "${namespace}" create secret generic test "${create_secret_args[@]}"
|
||||
|
||||
cat << 'EOF'
|
||||
|
||||
Remember to update your LokiStack CR with:
|
||||
spec:
|
||||
storage:
|
||||
tls:
|
||||
caName: openshift-service-ca.crt
|
||||
|
||||
EOF
|
||||
@@ -130,6 +130,7 @@ type LokiStackReconciler struct {
|
||||
// +kubebuilder:rbac:groups=config.openshift.io,resources=dnses;apiservers;proxies;clusterversions,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=route.openshift.io,resources=routes,verbs=get;list;watch;create;update;delete
|
||||
// +kubebuilder:rbac:groups=cloudcredential.openshift.io,resources=credentialsrequests,verbs=get;list;watch;create;update;delete
|
||||
// +kubebuilder:rbac:groups=discovery.k8s.io,resources=endpointslices,verbs=get;list;watch
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
|
||||
147
operator/internal/handlers/internal/networkpolicy/service.go
Normal file
147
operator/internal/handlers/internal/networkpolicy/service.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package networkpolicy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
discoveryv1 "k8s.io/api/discovery/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/grafana/loki/operator/internal/external/k8s"
|
||||
"github.com/grafana/loki/operator/internal/manifests/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingEndpointSlices = errors.New("no endpoint slices found for target object storage service")
|
||||
errMissingWantedPort = errors.New("couldn't resolve object storage service port to target Pod port")
|
||||
)
|
||||
|
||||
func ServicePortToPodPort(ctx context.Context, log logr.Logger, k k8s.Client, objStore storage.Options) ([]int32, error) {
|
||||
if objStore.S3 == nil {
|
||||
return []int32{}, nil
|
||||
}
|
||||
endpoint := objStore.S3.Endpoint
|
||||
|
||||
// Check if endpoint is a Kubernetes Service DNS name
|
||||
if !strings.Contains(endpoint, ".svc") {
|
||||
return []int32{}, nil
|
||||
}
|
||||
|
||||
serviceName, namespace, endpointPort, https := parseServiceEndpoint(endpoint)
|
||||
if serviceName == "" || namespace == "" {
|
||||
return []int32{}, nil // We do not error as the endpoint might not point to a Kubernetes Service
|
||||
}
|
||||
|
||||
service := &corev1.Service{}
|
||||
if err := k.Get(ctx, client.ObjectKey{Name: serviceName, Namespace: namespace}, service); err != nil {
|
||||
log.Info("failed to get Service for object storage", "service", serviceName, "namespace", namespace)
|
||||
return []int32{}, nil
|
||||
}
|
||||
|
||||
// List EndpointSlices for the service using the standard label
|
||||
endpointSlices := &discoveryv1.EndpointSliceList{}
|
||||
if err := k.List(ctx, endpointSlices, client.InNamespace(namespace), client.MatchingLabels{discoveryv1.LabelServiceName: serviceName}); err != nil {
|
||||
log.Error(err, "failed to list endpoint slices for target object storage service", "service", serviceName, "namespace", namespace)
|
||||
return []int32{}, err
|
||||
}
|
||||
|
||||
if len(endpointSlices.Items) == 0 {
|
||||
log.Error(errMissingEndpointSlices, "found no endpoint slices for service", "service", serviceName, "namespace", namespace)
|
||||
return []int32{}, errMissingEndpointSlices
|
||||
}
|
||||
|
||||
var targetPort int32
|
||||
wantedPort := int32(80)
|
||||
if https {
|
||||
wantedPort = 443
|
||||
}
|
||||
|
||||
if endpointPort > 0 {
|
||||
wantedPort = endpointPort // Override the wantedPort if specified in the endpoint
|
||||
for _, slice := range endpointSlices.Items {
|
||||
for _, p := range slice.Ports {
|
||||
if p.Port != nil && *p.Port == endpointPort {
|
||||
return []int32{*p.Port}, nil // If svc and pod have the same port then return it directly
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
targetPort = resolveTargetPort(service, endpointSlices, wantedPort)
|
||||
if targetPort == 0 {
|
||||
return []int32{}, errMissingWantedPort
|
||||
}
|
||||
|
||||
return []int32{targetPort}, nil
|
||||
}
|
||||
|
||||
func parseServiceEndpoint(endpoint string) (string, string, int32, bool) {
|
||||
https := strings.HasPrefix(endpoint, "https://")
|
||||
|
||||
var host string
|
||||
var portStr string
|
||||
if strings.HasPrefix(endpoint, "http://") || strings.HasPrefix(endpoint, "https://") {
|
||||
parsedURL, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return "", "", 0, false
|
||||
}
|
||||
host = parsedURL.Hostname()
|
||||
portStr = parsedURL.Port()
|
||||
} else {
|
||||
// Bare hostname:port format
|
||||
host = endpoint
|
||||
sHost, sPort, err := net.SplitHostPort(endpoint)
|
||||
if err == nil {
|
||||
host, portStr = sHost, sPort
|
||||
}
|
||||
}
|
||||
|
||||
parts := strings.Split(host, ".")
|
||||
if len(parts) < 3 {
|
||||
return "", "", 0, false
|
||||
}
|
||||
|
||||
serviceName := parts[0]
|
||||
namespace := parts[1]
|
||||
|
||||
var port int32
|
||||
if portStr != "" {
|
||||
p, err := strconv.ParseUint(portStr, 10, 16)
|
||||
if err != nil {
|
||||
return "", "", 0, false
|
||||
}
|
||||
port = int32(p)
|
||||
}
|
||||
|
||||
return serviceName, namespace, port, https
|
||||
}
|
||||
|
||||
func resolveTargetPort(service *corev1.Service, endpointSlices *discoveryv1.EndpointSliceList, endpointPort int32) int32 {
|
||||
for _, svcPort := range service.Spec.Ports {
|
||||
if svcPort.Port == endpointPort {
|
||||
for _, slice := range endpointSlices.Items {
|
||||
for _, p := range slice.Ports {
|
||||
switch svcPort.TargetPort.Type {
|
||||
case intstr.Int:
|
||||
if p.Port != nil && *p.Port == svcPort.TargetPort.IntVal {
|
||||
return *p.Port
|
||||
}
|
||||
case intstr.String:
|
||||
if p.Name != nil && *p.Name == svcPort.TargetPort.StrVal {
|
||||
return *p.Port
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
package networkpolicy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
discoveryv1 "k8s.io/api/discovery/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
"github.com/grafana/loki/operator/internal/manifests/storage"
|
||||
)
|
||||
|
||||
var (
|
||||
_ = corev1.AddToScheme(scheme.Scheme)
|
||||
_ = discoveryv1.AddToScheme(scheme.Scheme)
|
||||
)
|
||||
|
||||
func TestServicePortToPodPort(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
endpoint string
|
||||
expectedPort int32
|
||||
expectedError bool
|
||||
}{
|
||||
{
|
||||
name: "shortest svc endpoint without port",
|
||||
endpoint: "minio.test.svc",
|
||||
expectedPort: 8080,
|
||||
},
|
||||
{
|
||||
name: "svc endpoint without port",
|
||||
endpoint: "minio.test.svc.cluster.local",
|
||||
expectedPort: 8080,
|
||||
},
|
||||
{
|
||||
name: "https shortest svc endpoint",
|
||||
endpoint: "https://minio.test.svc",
|
||||
expectedPort: 6443,
|
||||
},
|
||||
{
|
||||
name: "https svc endpoint with port",
|
||||
endpoint: "https://minio.test.svc.cluster.local:443",
|
||||
expectedPort: 6443,
|
||||
},
|
||||
{
|
||||
name: "https svc endpoint with name",
|
||||
endpoint: "https://minio.test.svc.cluster.local:444",
|
||||
expectedPort: 6443,
|
||||
},
|
||||
{
|
||||
name: "https svc endpoint with invalid port",
|
||||
endpoint: "https://minio.test.svc.cluster.local:9999",
|
||||
expectedPort: 9999,
|
||||
expectedError: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
service := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio",
|
||||
Namespace: "test",
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Port: 443,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 6443,
|
||||
},
|
||||
},
|
||||
{
|
||||
Port: 444,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.String,
|
||||
StrVal: "https",
|
||||
},
|
||||
},
|
||||
{
|
||||
Port: 80,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
endpointSlice := &discoveryv1.EndpointSlice{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "minio-endpoint-slice",
|
||||
Namespace: "test",
|
||||
Labels: map[string]string{
|
||||
discoveryv1.LabelServiceName: "minio",
|
||||
},
|
||||
},
|
||||
Ports: []discoveryv1.EndpointPort{
|
||||
{
|
||||
Port: ptr.To(int32(8080)),
|
||||
},
|
||||
{
|
||||
Port: ptr.To(int32(6443)),
|
||||
Name: ptr.To("https"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
k := fake.NewClientBuilder().WithObjects(service, endpointSlice).
|
||||
WithScheme(scheme.Scheme).
|
||||
Build()
|
||||
|
||||
gotPorts, err := ServicePortToPodPort(context.Background(), logr.Discard(), k, storage.Options{
|
||||
S3: &storage.S3StorageConfig{
|
||||
Endpoint: tt.endpoint,
|
||||
},
|
||||
})
|
||||
if tt.expectedError {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []int32{tt.expectedPort}, gotPorts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseServiceEndpoint(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
endpoint string
|
||||
expectedServiceName string
|
||||
expectedNamespace string
|
||||
expectedPort int32
|
||||
expectedHTTPS bool
|
||||
}{
|
||||
{
|
||||
name: "shortest svc endpoint without port",
|
||||
endpoint: "minio.test.svc",
|
||||
expectedServiceName: "minio",
|
||||
expectedNamespace: "test",
|
||||
expectedPort: 0,
|
||||
expectedHTTPS: false,
|
||||
},
|
||||
{
|
||||
name: "shortest svc endpoint with port",
|
||||
endpoint: "minio.test.svc:443",
|
||||
expectedServiceName: "minio",
|
||||
expectedNamespace: "test",
|
||||
expectedPort: 443,
|
||||
expectedHTTPS: false,
|
||||
},
|
||||
{
|
||||
name: "svc endpoint with port",
|
||||
endpoint: "minio.test.svc.cluster.local:443",
|
||||
expectedServiceName: "minio",
|
||||
expectedNamespace: "test",
|
||||
expectedPort: 443,
|
||||
expectedHTTPS: false,
|
||||
},
|
||||
{
|
||||
name: "http svc endpoint without port",
|
||||
endpoint: "http://minio.test.svc.cluster.local",
|
||||
expectedServiceName: "minio",
|
||||
expectedNamespace: "test",
|
||||
expectedPort: 0,
|
||||
expectedHTTPS: false,
|
||||
},
|
||||
{
|
||||
name: "https svc endpoint without port",
|
||||
endpoint: "https://minio.test.svc.cluster.local",
|
||||
expectedServiceName: "minio",
|
||||
expectedNamespace: "test",
|
||||
expectedPort: 0,
|
||||
expectedHTTPS: true,
|
||||
},
|
||||
{
|
||||
name: "https svc endpoint with port",
|
||||
endpoint: "https://minio.test.svc.cluster.local:443",
|
||||
expectedServiceName: "minio",
|
||||
expectedNamespace: "test",
|
||||
expectedPort: 443,
|
||||
expectedHTTPS: true,
|
||||
},
|
||||
{
|
||||
name: "shortest https svc endpoint with port",
|
||||
endpoint: "https://minio.test.svc:443",
|
||||
expectedServiceName: "minio",
|
||||
expectedNamespace: "test",
|
||||
expectedPort: 443,
|
||||
expectedHTTPS: true,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotServiceName, gotNamespace, gotPort, gotHTTPS := parseServiceEndpoint(tt.endpoint)
|
||||
require.Equal(t, tt.expectedServiceName, gotServiceName)
|
||||
require.Equal(t, tt.expectedNamespace, gotNamespace)
|
||||
require.Equal(t, tt.expectedPort, gotPort)
|
||||
require.Equal(t, tt.expectedHTTPS, gotHTTPS)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveTargetPort(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
service *corev1.Service
|
||||
slices *discoveryv1.EndpointSliceList
|
||||
endpointPort int32
|
||||
expectedPort int32
|
||||
}{
|
||||
{
|
||||
name: "simple matching port",
|
||||
service: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
slices: &discoveryv1.EndpointSliceList{
|
||||
Items: []discoveryv1.EndpointSlice{
|
||||
{
|
||||
Ports: []discoveryv1.EndpointPort{
|
||||
{Port: ptr.To(int32(80))},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpointPort: 80,
|
||||
expectedPort: 80,
|
||||
},
|
||||
{
|
||||
name: "targetPort diff from svc port but matching port",
|
||||
service: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 8080,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
slices: &discoveryv1.EndpointSliceList{
|
||||
Items: []discoveryv1.EndpointSlice{
|
||||
{
|
||||
Ports: []discoveryv1.EndpointPort{
|
||||
{Port: ptr.To(int32(8080))},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpointPort: 80,
|
||||
expectedPort: 8080,
|
||||
},
|
||||
{
|
||||
name: "targetPort diff from svc port but matching name",
|
||||
service: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.String,
|
||||
StrVal: "http",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
slices: &discoveryv1.EndpointSliceList{
|
||||
Items: []discoveryv1.EndpointSlice{
|
||||
{
|
||||
Ports: []discoveryv1.EndpointPort{
|
||||
{
|
||||
Port: ptr.To(int32(8080)),
|
||||
Name: ptr.To("http"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpointPort: 80,
|
||||
expectedPort: 8080,
|
||||
},
|
||||
{
|
||||
name: "no matching port or name",
|
||||
service: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Port: 80,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 443,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
slices: &discoveryv1.EndpointSliceList{
|
||||
Items: []discoveryv1.EndpointSlice{
|
||||
{
|
||||
Ports: []discoveryv1.EndpointPort{
|
||||
{
|
||||
Port: ptr.To(int32(8080)),
|
||||
Name: ptr.To("http"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpointPort: 80,
|
||||
expectedPort: 0,
|
||||
},
|
||||
{
|
||||
name: "no matching service port",
|
||||
service: &corev1.Service{
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Port: 443,
|
||||
TargetPort: intstr.IntOrString{
|
||||
Type: intstr.Int,
|
||||
IntVal: 6443,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
slices: &discoveryv1.EndpointSliceList{
|
||||
Items: []discoveryv1.EndpointSlice{
|
||||
{
|
||||
Ports: []discoveryv1.EndpointPort{
|
||||
{Port: ptr.To(int32(6443))},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
endpointPort: 80,
|
||||
expectedPort: 0,
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := resolveTargetPort(tt.service, tt.slices, tt.endpointPort)
|
||||
require.Equal(t, tt.expectedPort, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -133,6 +133,12 @@ func CreateOrUpdateLokiStack(
|
||||
networkPolicyRuleSet := lokiv1.NetworkPolicyRuleSetNone
|
||||
if stack.Spec.NetworkPolicies != nil {
|
||||
networkPolicyRuleSet = stack.Spec.NetworkPolicies.RuleSet
|
||||
|
||||
ports, optErr := networkpolicy.ServicePortToPodPort(ctx, ll, k, objStore)
|
||||
if optErr != nil {
|
||||
return nil, optErr
|
||||
}
|
||||
opts.NetworkPolicyObjStorePorts = ports
|
||||
}
|
||||
|
||||
tlsProfileType := configv1.TLSProfileType(fg.TLSProfile)
|
||||
|
||||
@@ -326,9 +326,13 @@ func buildLokiAllowGatewayIngress(opts Options) *networkingv1.NetworkPolicy {
|
||||
// components that need to access object storage to object storage
|
||||
func buildLokiAllowBucketEgress(opts Options) *networkingv1.NetworkPolicy {
|
||||
objstorePort := []int32{443} // Default HTTPS port
|
||||
|
||||
if ports := getEndpointPort(opts.ObjectStorage, opts.Gates.OpenShift.Enabled); len(ports) > 0 {
|
||||
objstorePort = ports
|
||||
switch {
|
||||
case len(opts.NetworkPolicyObjStorePorts) > 0:
|
||||
objstorePort = opts.NetworkPolicyObjStorePorts
|
||||
default:
|
||||
if ports := getEndpointPort(opts.ObjectStorage, opts.Gates.OpenShift.Enabled); len(ports) > 0 {
|
||||
objstorePort = ports
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Stack.Proxy != nil {
|
||||
|
||||
@@ -41,6 +41,8 @@ type Options struct {
|
||||
Tenants Tenants
|
||||
|
||||
TLSProfile TLSProfileSpec
|
||||
|
||||
NetworkPolicyObjStorePorts []int32
|
||||
}
|
||||
|
||||
// GatewayTimeoutConfig contains the http server configuration options for all Loki components.
|
||||
|
||||
Reference in New Issue
Block a user