Skip to content

Commit 7df1bee

Browse files
author
Sanskar Jaiswal
committed
Add flag to disable cross namespace refs to AlertProviders and MetricTemplates
Signed-off-by: Sanskar Jaiswal <[email protected]>
1 parent a1e519b commit 7df1bee

14 files changed

+117
-73
lines changed

cmd/flagger/main.go

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ var (
8484
ver bool
8585
kubeconfigServiceMesh string
8686
clusterName string
87+
noCrossNamespaceRefs bool
8788
)
8889

8990
func init() {
@@ -117,6 +118,7 @@ func init() {
117118
flag.BoolVar(&ver, "version", false, "Print version")
118119
flag.StringVar(&kubeconfigServiceMesh, "kubeconfig-service-mesh", "", "Path to a kubeconfig for the service mesh control plane cluster.")
119120
flag.StringVar(&clusterName, "cluster-name", "", "Cluster name to be included in alert msgs.")
121+
flag.BoolVar(&noCrossNamespaceRefs, "no-cross-namespace-refs", false, "When set to true, Flagger can only refer to resources in the same namespace.")
120122
}
121123

122124
func main() {
@@ -241,6 +243,7 @@ func main() {
241243
version.VERSION,
242244
fromEnv("EVENT_WEBHOOK_URL", eventWebhook),
243245
clusterName,
246+
noCrossNamespaceRefs,
244247
)
245248

246249
// leader election context

pkg/apis/flagger/v1beta1/canary.go

+18-3
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,15 @@ type CanarySpec struct {
7070
MetricsServer string `json:"metricsServer,omitempty"`
7171

7272
// TargetRef references a target resource
73-
TargetRef CrossNamespaceObjectReference `json:"targetRef"`
73+
TargetRef LocalObjectReference `json:"targetRef"`
7474

7575
// AutoscalerRef references an autoscaling resource
7676
// +optional
77-
AutoscalerRef *CrossNamespaceObjectReference `json:"autoscalerRef,omitempty"`
77+
AutoscalerRef *LocalObjectReference `json:"autoscalerRef,omitempty"`
7878

7979
// Reference to NGINX ingress resource
8080
// +optional
81-
IngressRef *CrossNamespaceObjectReference `json:"ingressRef,omitempty"`
81+
IngressRef *LocalObjectReference `json:"ingressRef,omitempty"`
8282

8383
// Reference to Gloo Upstream resource. Upstream config is copied from
8484
// the referenced upstream to the upstreams generated by flagger.
@@ -393,6 +393,21 @@ type CrossNamespaceObjectReference struct {
393393
Namespace string `json:"namespace,omitempty"`
394394
}
395395

396+
// LocalObjectReference contains enough information to let you locate the typed
397+
// referenced object in the same namespace.
398+
type LocalObjectReference struct {
399+
// API version of the referent
400+
// +optional
401+
APIVersion string `json:"apiVersion,omitempty"`
402+
403+
// Kind of the referent
404+
// +optional
405+
Kind string `json:"kind,omitempty"`
406+
407+
// Name of the referent
408+
Name string `json:"name"`
409+
}
410+
396411
// CustomMetadata holds labels and annotations to set on generated objects.
397412
type CustomMetadata struct {
398413
Labels map[string]string `json:"labels,omitempty"`

pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go

+18-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/canary/daemonset_fixture_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ func newDaemonSetControllerTestCanary(dc daemonsetConfigs) *flaggerv1.Canary {
352352
Name: "podinfo",
353353
},
354354
Spec: flaggerv1.CanarySpec{
355-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
355+
TargetRef: flaggerv1.LocalObjectReference{
356356
Name: dc.name,
357357
APIVersion: "apps/v1",
358358
Kind: "DaemonSet",

pkg/canary/deployment_fixture_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,12 @@ func newDeploymentControllerTestCanary(cc canaryConfigs) *flaggerv1.Canary {
392392
Name: "podinfo",
393393
},
394394
Spec: flaggerv1.CanarySpec{
395-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
395+
TargetRef: flaggerv1.LocalObjectReference{
396396
Name: cc.targetName,
397397
APIVersion: "apps/v1",
398398
Kind: "Deployment",
399399
},
400-
AutoscalerRef: &flaggerv1.CrossNamespaceObjectReference{
400+
AutoscalerRef: &flaggerv1.LocalObjectReference{
401401
Name: "podinfo",
402402
APIVersion: "autoscaling/v2beta2",
403403
Kind: "HorizontalPodAutoscaler",

pkg/controller/controller.go

+39-36
Original file line numberDiff line numberDiff line change
@@ -49,24 +49,25 @@ const controllerAgentName = "flagger"
4949

5050
// Controller is managing the canary objects and schedules canary deployments
5151
type Controller struct {
52-
kubeClient kubernetes.Interface
53-
flaggerClient clientset.Interface
54-
flaggerInformers Informers
55-
flaggerSynced cache.InformerSynced
56-
flaggerWindow time.Duration
57-
workqueue workqueue.RateLimitingInterface
58-
eventRecorder record.EventRecorder
59-
logger *zap.SugaredLogger
60-
canaries *sync.Map
61-
jobs map[string]CanaryJob
62-
recorder metrics.Recorder
63-
notifier notifier.Interface
64-
canaryFactory *canary.Factory
65-
routerFactory *router.Factory
66-
observerFactory *observers.Factory
67-
meshProvider string
68-
eventWebhook string
69-
clusterName string
52+
kubeClient kubernetes.Interface
53+
flaggerClient clientset.Interface
54+
flaggerInformers Informers
55+
flaggerSynced cache.InformerSynced
56+
flaggerWindow time.Duration
57+
workqueue workqueue.RateLimitingInterface
58+
eventRecorder record.EventRecorder
59+
logger *zap.SugaredLogger
60+
canaries *sync.Map
61+
jobs map[string]CanaryJob
62+
recorder metrics.Recorder
63+
notifier notifier.Interface
64+
canaryFactory *canary.Factory
65+
routerFactory *router.Factory
66+
observerFactory *observers.Factory
67+
meshProvider string
68+
eventWebhook string
69+
clusterName string
70+
noCrossNamespaceRefs bool
7071
}
7172

7273
type Informers struct {
@@ -89,6 +90,7 @@ func NewController(
8990
version string,
9091
eventWebhook string,
9192
clusterName string,
93+
noCrossNamespaceRefs bool,
9294
) *Controller {
9395
logger.Debug("Creating event broadcaster")
9496
flaggerscheme.AddToScheme(scheme.Scheme)
@@ -103,24 +105,25 @@ func NewController(
103105
recorder.SetInfo(version, meshProvider)
104106

105107
ctrl := &Controller{
106-
kubeClient: kubeClient,
107-
flaggerClient: flaggerClient,
108-
flaggerInformers: flaggerInformers,
109-
flaggerSynced: flaggerInformers.CanaryInformer.Informer().HasSynced,
110-
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), controllerAgentName),
111-
eventRecorder: eventRecorder,
112-
logger: logger,
113-
canaries: new(sync.Map),
114-
jobs: map[string]CanaryJob{},
115-
flaggerWindow: flaggerWindow,
116-
observerFactory: observerFactory,
117-
recorder: recorder,
118-
notifier: notifier,
119-
canaryFactory: canaryFactory,
120-
routerFactory: routerFactory,
121-
meshProvider: meshProvider,
122-
eventWebhook: eventWebhook,
123-
clusterName: clusterName,
108+
kubeClient: kubeClient,
109+
flaggerClient: flaggerClient,
110+
flaggerInformers: flaggerInformers,
111+
flaggerSynced: flaggerInformers.CanaryInformer.Informer().HasSynced,
112+
workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), controllerAgentName),
113+
eventRecorder: eventRecorder,
114+
logger: logger,
115+
canaries: new(sync.Map),
116+
jobs: map[string]CanaryJob{},
117+
flaggerWindow: flaggerWindow,
118+
observerFactory: observerFactory,
119+
recorder: recorder,
120+
notifier: notifier,
121+
canaryFactory: canaryFactory,
122+
routerFactory: routerFactory,
123+
meshProvider: meshProvider,
124+
eventWebhook: eventWebhook,
125+
clusterName: clusterName,
126+
noCrossNamespaceRefs: noCrossNamespaceRefs,
124127
}
125128

126129
flaggerInformers.CanaryInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{

pkg/controller/events.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,12 @@ func (c *Controller) alert(canary *flaggerv1.Canary, message string, metadata bo
108108

109109
// determine alert provider namespace
110110
providerNamespace := canary.GetNamespace()
111-
if alert.ProviderRef.Namespace != "" {
111+
if alert.ProviderRef.Namespace != canary.Namespace {
112+
if c.noCrossNamespaceRefs {
113+
c.logger.With("canary", fmt.Sprintf("%s.%s", canary.Name, canary.Namespace)).
114+
Errorf("can't access alert provider ref %s.%s, cross-namespace references are blocked", alert.ProviderRef.Name, alert.ProviderRef.Namespace)
115+
return
116+
}
112117
providerNamespace = alert.ProviderRef.Namespace
113118
}
114119

pkg/controller/scheduler_daemonset_fixture_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ func newDaemonSetTestCanary() *flaggerv1.Canary {
261261
Name: "podinfo",
262262
},
263263
Spec: flaggerv1.CanarySpec{
264-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
264+
TargetRef: flaggerv1.LocalObjectReference{
265265
Name: "podinfo",
266266
APIVersion: "apps/v1",
267267
Kind: "DaemonSet",
@@ -318,7 +318,7 @@ func newDaemonSetTestCanaryAB() *flaggerv1.Canary {
318318
Name: "podinfo",
319319
},
320320
Spec: flaggerv1.CanarySpec{
321-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
321+
TargetRef: flaggerv1.LocalObjectReference{
322322
Name: "podinfo",
323323
APIVersion: "apps/v1",
324324
Kind: "DaemonSet",

pkg/controller/scheduler_deployment_fixture_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -289,12 +289,12 @@ func newDeploymentTestCanary() *flaggerv1.Canary {
289289
Name: "podinfo",
290290
},
291291
Spec: flaggerv1.CanarySpec{
292-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
292+
TargetRef: flaggerv1.LocalObjectReference{
293293
Name: "podinfo",
294294
APIVersion: "apps/v1",
295295
Kind: "Deployment",
296296
},
297-
AutoscalerRef: &flaggerv1.CrossNamespaceObjectReference{
297+
AutoscalerRef: &flaggerv1.LocalObjectReference{
298298
Name: "podinfo",
299299
APIVersion: "autoscaling/v2beta2",
300300
Kind: "HorizontalPodAutoscaler",
@@ -351,12 +351,12 @@ func newDeploymentTestCanaryAB() *flaggerv1.Canary {
351351
Name: "podinfo",
352352
},
353353
Spec: flaggerv1.CanarySpec{
354-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
354+
TargetRef: flaggerv1.LocalObjectReference{
355355
Name: "podinfo",
356356
APIVersion: "apps/v1",
357357
Kind: "Deployment",
358358
},
359-
AutoscalerRef: &flaggerv1.CrossNamespaceObjectReference{
359+
AutoscalerRef: &flaggerv1.LocalObjectReference{
360360
Name: "podinfo",
361361
APIVersion: "autoscaling/v2beta2",
362362
Kind: "HorizontalPodAutoscaler",

pkg/controller/scheduler_metrics.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ func (c *Controller) checkMetricProviderAvailability(canary *flaggerv1.Canary) e
5454

5555
if metric.TemplateRef != nil {
5656
namespace := canary.Namespace
57-
if metric.TemplateRef.Namespace != "" {
57+
if metric.TemplateRef.Namespace != canary.Namespace {
58+
if c.noCrossNamespaceRefs {
59+
return fmt.Errorf("can't access metric template ref %s.%s, cross-namespace references are blocked", metric.TemplateRef.Name, metric.TemplateRef.Namespace)
60+
}
5861
namespace = metric.TemplateRef.Namespace
5962
}
6063

@@ -238,7 +241,11 @@ func (c *Controller) runMetricChecks(canary *flaggerv1.Canary) bool {
238241
for _, metric := range canary.GetAnalysis().Metrics {
239242
if metric.TemplateRef != nil {
240243
namespace := canary.Namespace
241-
if metric.TemplateRef.Namespace != "" {
244+
if metric.TemplateRef.Namespace != canary.Namespace {
245+
if c.noCrossNamespaceRefs {
246+
c.recordEventErrorf(canary, "Metric template %s.%s error: cross-namespace references are blocked", metric.TemplateRef.Name, metric.TemplateRef.Namespace)
247+
return false
248+
}
242249
namespace = metric.TemplateRef.Namespace
243250
}
244251

pkg/controller/scheduler_metrics_test.go

+4
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,9 @@ func TestController_checkMetricProviderAvailability(t *testing.T) {
6565
Namespace: "default",
6666
}
6767
require.NoError(t, ctrl.checkMetricProviderAvailability(canary))
68+
69+
ctrl.noCrossNamespaceRefs = true
70+
canary.Namespace = "test"
71+
require.Error(t, ctrl.checkMetricProviderAvailability(canary))
6872
})
6973
}

pkg/controller/scheduler_svc_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ func newTestServiceCanary() *flaggerv1.Canary {
123123
Name: "podinfo",
124124
},
125125
Spec: flaggerv1.CanarySpec{
126-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
126+
TargetRef: flaggerv1.LocalObjectReference{
127127
Name: "podinfo",
128128
APIVersion: "core/v1",
129129
Kind: "Service",
@@ -161,7 +161,7 @@ func newTestServiceCanaryMaxWeight() *flaggerv1.Canary {
161161
Name: "podinfo",
162162
},
163163
Spec: flaggerv1.CanarySpec{
164-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
164+
TargetRef: flaggerv1.LocalObjectReference{
165165
Name: "podinfo",
166166
APIVersion: "core/v1",
167167
Kind: "Service",
@@ -199,7 +199,7 @@ func newTestServiceCanaryWithWeightsHappyCase() *flaggerv1.Canary {
199199
Name: "podinfo",
200200
},
201201
Spec: flaggerv1.CanarySpec{
202-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
202+
TargetRef: flaggerv1.LocalObjectReference{
203203
Name: "podinfo",
204204
APIVersion: "core/v1",
205205
Kind: "Service",
@@ -236,7 +236,7 @@ func newTestServiceCanaryWithWeightsOverflow() *flaggerv1.Canary {
236236
Name: "podinfo",
237237
},
238238
Spec: flaggerv1.CanarySpec{
239-
TargetRef: flaggerv1.CrossNamespaceObjectReference{
239+
TargetRef: flaggerv1.LocalObjectReference{
240240
Name: "podinfo",
241241
APIVersion: "core/v1",
242242
Kind: "Service",

pkg/router/gateway_api.go

-9
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,6 @@ func (gwr *GatewayAPIRouter) Reconcile(canary *flaggerv1.Canary) error {
6464
apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames()
6565

6666
hrNamespace := canary.Namespace
67-
if canary.Spec.TargetRef.Namespace != "" {
68-
hrNamespace = canary.Spec.TargetRef.Namespace
69-
}
7067

7168
hostNames := []v1alpha2.Hostname{}
7269
for _, host := range canary.Spec.Service.Hosts {
@@ -193,9 +190,6 @@ func (gwr *GatewayAPIRouter) GetRoutes(canary *flaggerv1.Canary) (
193190
) {
194191
apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames()
195192
hrNamespace := canary.Namespace
196-
if canary.Spec.TargetRef.Namespace != "" {
197-
hrNamespace = canary.Spec.TargetRef.Namespace
198-
}
199193
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{})
200194
if err != nil {
201195
err = fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err)
@@ -228,9 +222,6 @@ func (gwr *GatewayAPIRouter) SetRoutes(
228222
cWeight := int32(canaryWeight)
229223
apexSvcName, primarySvcName, canarySvcName := canary.GetServiceNames()
230224
hrNamespace := canary.Namespace
231-
if canary.Spec.TargetRef.Namespace != "" {
232-
hrNamespace = canary.Spec.TargetRef.Namespace
233-
}
234225
httpRoute, err := gwr.gatewayAPIClient.GatewayapiV1alpha2().HTTPRoutes(hrNamespace).Get(context.TODO(), apexSvcName, metav1.GetOptions{})
235226
if err != nil {
236227
return fmt.Errorf("HTTPRoute %s.%s get error: %w", apexSvcName, hrNamespace, err)

0 commit comments

Comments
 (0)