Skip to content

Commit ef8a254

Browse files
committed
feat(controller): hard fork
Signed-off-by: Oliver Bähler <[email protected]>
1 parent 2da7ea4 commit ef8a254

File tree

9 files changed

+109
-29
lines changed

9 files changed

+109
-29
lines changed

charts/cortex-proxy/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ The following Values are available for this chart.
6666

6767
| Key | Type | Default | Description |
6868
|-----|------|---------|-------------|
69+
| config.backend | object | `{"auth":{"password":"","username":""},"url":"http://cortex-distributor.cortex.svc:8080/api/v1/push"}` | Configure the backend to redirect all the requests to |
6970
| config.backend.auth.password | string | `""` | Password |
7071
| config.backend.auth.username | string | `""` | Username |
7172
| config.backend.url | string | `"http://cortex-distributor.cortex.svc:8080/api/v1/push"` | Where to send the modified requests (Cortex) |
@@ -74,6 +75,7 @@ The following Values are available for this chart.
7475
| config.maxConnectionDuration | string | `"0s"` | Maximum duration to keep outgoing connections alive (to Cortex/Mimir) Useful for resetting L4 load-balancer state Use 0 to keep them indefinitely |
7576
| config.maxConnectionsPerHost | int | `64` | This parameter sets the limit for the count of outgoing concurrent connections to Cortex / Mimir. By default it's 64 and if all of these connections are busy you will get errors when pushing from Prometheus. If your `target` is a DNS name that resolves to several IPs then this will be a per-IP limit. |
7677
| config.metadata | bool | `false` | Whether to forward metrics metadata from Prometheus to Cortex Since metadata requests have no timeseries in them - we cannot divide them into tenants So the metadata requests will be sent to the default tenant only, if one is not defined - they will be dropped |
78+
| config.selector | object | `{}` | Specify which tenants should be selected for this proxy. Tenants not matching the labels are not considered by the controller. |
7779
| config.tenant.acceptAll | bool | `false` | Enable if you want all metrics from Prometheus to be accepted with a 204 HTTP code regardless of the response from Cortex. This can lose metrics if Cortex is throwing rejections. |
7880
| config.tenant.default | string | `"cortex-tenant-default"` | Which tenant ID to use if the label is missing in any of the timeseries If this is not set or empty then the write request with missing tenant label will be rejected with HTTP code 400 |
7981
| config.tenant.header | string | `"X-Scope-OrgID"` | To which header to add the tenant ID |

charts/cortex-proxy/values.schema.json

+4
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@
9090
"metadata": {
9191
"type": "boolean"
9292
},
93+
"selector": {
94+
"properties": {},
95+
"type": "object"
96+
},
9397
"tenant": {
9498
"properties": {
9599
"acceptAll": {

charts/cortex-proxy/values.yaml

+5-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ config:
3232
# By default it's 64 and if all of these connections are busy you will get errors when pushing from Prometheus.
3333
# If your `target` is a DNS name that resolves to several IPs then this will be a per-IP limit.
3434
maxConnectionsPerHost: 64
35-
35+
# -- Configure the backend to redirect all the requests to
3636
backend:
3737
# -- Where to send the modified requests (Cortex)
3838
url: http://cortex-distributor.cortex.svc:8080/api/v1/push
@@ -42,7 +42,10 @@ config:
4242
username: ""
4343
# -- Password
4444
password: ""
45-
45+
# -- Specify which tenants should be selected for this proxy.
46+
# Tenants not matching the labels are not considered by the controller.
47+
selector: {}
48+
# Tenant Properties
4649
tenant:
4750
# -- List of labels examined for tenant information. If set takes precedent over `label`
4851
labels: []

cmd/main.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ func main() {
100100
metricsRecorder := metrics.MustMakeRecorder()
101101

102102
tenants := &controllers.TenantController{
103-
Client: mgr.GetClient(),
104-
Scheme: mgr.GetScheme(),
103+
Client: mgr.GetClient(),
104+
Scheme: mgr.GetScheme(),
105+
Selector: cfg.Selector.Selector(),
105106
// Log: ctrl.Log.WithName("Store").WithName("Config"),
106107
Metrics: metricsRecorder,
107108
Store: store,

config.yml

+11-15
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
1-
listen: 0.0.0.0:8080
2-
31
backend:
42
url: http://127.0.0.1:9091/receive
53
auth:
6-
egress:
7-
username: foo
8-
password: bar
9-
10-
enable_ipv6: false
11-
max_conns_per_host: 64
12-
4+
username: foo
5+
password: bar
6+
# selector:
7+
# matchLabels:
8+
# test: me
9+
ipv6: false
10+
maxConnectionsPerHost: 64
1311
timeout: 10s
14-
timeout_shutdown: 0s
12+
timeoutShutdown: 0s
1513
concurrency: 10
1614
metadata: false
17-
log_response_errors: true
18-
1915
tenant:
2016
labels:
2117
- tenant
2218
- other_tenant
2319
prefix: ""
24-
prefix_prefer_source: false
25-
label_remove: true
20+
prefixPreferSource: false
21+
labelRemove: true
2622
header: X-Scope-OrgID
2723
default: ""
28-
accept_all: false
24+
acceptAll: false

internal/config/config.go

+2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ type Config struct {
1515

1616
EnableIPv6 bool `yaml:"ipv6"`
1717

18+
Selector LabelSelector `yaml:"selector,omitempty"`
19+
1820
Timeout time.Duration `yaml:"timeout"`
1921
TimeoutShutdown time.Duration `yaml:"timeoutShutdown"`
2022
Concurrency int `yaml:"concurrency"`

internal/config/config_test.go

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ timeout: 7s
3535
timeoutShutdown: 2s
3636
maxConnectionDuration: 10s
3737
maxConnectionsPerHost: 128
38+
selector:
39+
matchLabels:
40+
special: "tenants"
3841
`
3942
tmpFile, err := os.CreateTemp("", "config-test-*.yaml")
4043
Expect(err).NotTo(HaveOccurred())

internal/config/selector.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package config
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
)
6+
7+
type LabelSelector struct {
8+
MatchLabels map[string]string `yaml:"matchLabels,omitempty"`
9+
MatchExpressions []LabelSelectorRequirement `yaml:"matchExpressions,omitempty"`
10+
}
11+
12+
type LabelSelectorRequirement struct {
13+
Key string `yaml:"key"`
14+
Operator string `yaml:"operator"`
15+
Values []string `yaml:"values,omitempty"`
16+
}
17+
18+
func (l *LabelSelector) Selector() *metav1.LabelSelector {
19+
r := &metav1.LabelSelector{
20+
MatchLabels: l.MatchLabels,
21+
}
22+
for _, req := range l.MatchExpressions {
23+
r.MatchExpressions = append(r.MatchExpressions, metav1.LabelSelectorRequirement{
24+
Key: req.Key,
25+
Operator: metav1.LabelSelectorOperator(req.Operator),
26+
Values: req.Values,
27+
})
28+
}
29+
30+
return r
31+
}

internal/controllers/reconciler.go

+48-10
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,50 @@ import (
99
"github.com/projectcapsule/cortex-proxy/internal/metrics"
1010
"github.com/projectcapsule/cortex-proxy/internal/stores"
1111
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
"k8s.io/apimachinery/pkg/labels"
1213
"k8s.io/apimachinery/pkg/runtime"
1314
ctrl "sigs.k8s.io/controller-runtime"
1415
"sigs.k8s.io/controller-runtime/pkg/client"
16+
"sigs.k8s.io/controller-runtime/pkg/event"
17+
"sigs.k8s.io/controller-runtime/pkg/predicate"
1518
)
1619

17-
// CapsuleArgocdReconciler reconciles a CapsuleArgocd object.
1820
type TenantController struct {
1921
client.Client
20-
Metrics *metrics.Recorder
21-
Scheme *runtime.Scheme
22-
Store *stores.TenantStore
23-
Log logr.Logger
22+
Metrics *metrics.Recorder
23+
Scheme *runtime.Scheme
24+
Store *stores.TenantStore
25+
Log logr.Logger
26+
Selector *metav1.LabelSelector
2427
}
2528

2629
func (r *TenantController) SetupWithManager(mgr ctrl.Manager) error {
27-
return ctrl.NewControllerManagedBy(mgr).
28-
For(&capsulev1beta2.Tenant{}).
29-
Complete(r)
30+
builder := ctrl.NewControllerManagedBy(mgr).For(&capsulev1beta2.Tenant{})
31+
32+
// If a selector is provided, add an event filter so that only matching tenants trigger reconcile.
33+
if r.Selector != nil {
34+
selector, err := metav1.LabelSelectorAsSelector(r.Selector)
35+
if err != nil {
36+
return fmt.Errorf("invalid label selector: %w", err)
37+
}
38+
39+
builder = builder.WithEventFilter(predicate.Funcs{
40+
CreateFunc: func(e event.CreateEvent) bool {
41+
return selector.Matches(labels.Set(e.Object.GetLabels()))
42+
},
43+
UpdateFunc: func(e event.UpdateEvent) bool {
44+
return selector.Matches(labels.Set(e.ObjectNew.GetLabels()))
45+
},
46+
DeleteFunc: func(e event.DeleteEvent) bool {
47+
return selector.Matches(labels.Set(e.Object.GetLabels()))
48+
},
49+
GenericFunc: func(e event.GenericEvent) bool {
50+
return selector.Matches(labels.Set(e.Object.GetLabels()))
51+
},
52+
})
53+
}
54+
55+
return builder.Complete(r)
3056
}
3157

3258
func (r *TenantController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
@@ -48,10 +74,22 @@ func (r *TenantController) Reconcile(ctx context.Context, req ctrl.Request) (ctr
4874
}
4975

5076
// First execttion of the controller to load the settings (without manager cache).
51-
func (r *TenantController) Init(ctx context.Context, client client.Client) (err error) {
77+
func (r *TenantController) Init(ctx context.Context, c client.Client) (err error) {
5278
tnts := &capsulev1beta2.TenantList{}
5379

54-
if err := client.List(ctx, tnts); err != nil {
80+
var opts []client.ListOption
81+
82+
// If a selector is provided, add it as a list option.
83+
if r.Selector != nil {
84+
selector, err := metav1.LabelSelectorAsSelector(r.Selector)
85+
if err != nil {
86+
return fmt.Errorf("invalid label selector: %w", err)
87+
}
88+
89+
opts = append(opts, client.MatchingLabelsSelector{Selector: selector})
90+
}
91+
92+
if err := c.List(ctx, tnts, opts...); err != nil {
5593
return fmt.Errorf("could not load tenants: %w", err)
5694
}
5795

0 commit comments

Comments
 (0)