Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: deploy operator in single namespace without ClusterRole #5807

Closed
wants to merge 13 commits into from

Conversation

zhuangzhi-cc
Copy link

@zhuangzhi-cc zhuangzhi-cc commented Aug 14, 2023

Description

We need deploy prometheus operator in single namespace without cluster role rabc. Follow simonpasquier's suggestion if the --namespaces is a single namespace, operator would running in none clusterrole mode.
eg. --namespaces=monitoring

Type of change

What type of changes does your code introduce to the Prometheus operator? Put an x in the box that apply.

  • CHANGE (fix or feature that would cause existing functionality to not work as expected)
  • FEATURE (non-breaking change which adds functionality)
  • BUGFIX (non-breaking change which fixes an issue)
  • ENHANCEMENT (non-breaking change which improves existing functionality)
  • NONE (if none of the other choices apply. Example, tooling, build system, CI, docs, etc.)

Changelog entry

Please put a one-line changelog entry below. This will be copied to the changelog file during the release process.

  1. add single namespace to namespaces argument
  2. Createo role rbac instead of ClusterRole
  3. deploy prometheus operator, prometheus and application PODs in same namespace.

@zhuangzhi-cc zhuangzhi-cc requested a review from a team as a code owner August 14, 2023 12:49
@zhuangzhi-cc zhuangzhi-cc changed the title Singlens deploy operator in single namespace without ClusterRole Aug 17, 2023
@zhuangzhi-cc zhuangzhi-cc changed the title deploy operator in single namespace without ClusterRole enhancement: deploy operator in single namespace without ClusterRole Aug 17, 2023
),
&v1.Namespace{}, nsResyncPeriod, cache.Indexers{},
)
if !operator.IsSingleNamespace(c.config.Namespaces) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that the test should be more elaborated and dependent on the controller: e.g. Alertmanager can skip the namespace informers if AlertmanagerAllowList and AlertmanagerConfigAllowList have a single item (and different from v1.NamespaceAll).

For Prometheus and Thanos, the checks will be different.

Copy link
Author

Choose a reason for hiding this comment

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

ok, I would change it.

Copy link
Author

Choose a reason for hiding this comment

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

code changed

@@ -969,11 +978,15 @@ func (c *Operator) handleConfigMapUpdate(old, cur interface{}) {
}

func (c *Operator) enqueueForPrometheusNamespace(nsName string) {
c.enqueueForNamespace(c.nsPromInf.GetStore(), nsName)
if !operator.IsSingleNamespace(c.config.Namespaces) {
c.enqueueForNamespace(c.nsPromInf.GetStore(), nsName)
Copy link
Contributor

Choose a reason for hiding this comment

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

We still need to run the reconciliation. I think that the change should rather be to enqueueForNamespace().

Copy link
Author

Choose a reason for hiding this comment

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

Yes, it woule be more simplefy, I would change it.

Copy link
Author

Choose a reason for hiding this comment

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

code changed

@@ -521,7 +530,7 @@ func (c *Operator) Run(ctx context.Context) error {

c.addHandlers()

if c.kubeletSyncEnabled {
if c.kubeletSyncEnabled && !operator.IsSingleNamespace(c.config.Namespaces) {
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need this?

Copy link
Author

Choose a reason for hiding this comment

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

level=error ts=2023-09-01T11:39:50.560491434Z caller=operator.go:626 component=prometheusoperator msg="Syncing nodes into Endpoints object failed" err="listing nodes failed: nodes is forbidden: User "system:serviceaccount:rio:prometheus-operator" cannot list resource "nodes" in API group "" at the cluster scope"
That's the error log if disable this limit.

Copy link
Contributor

@simonpasquier simonpasquier left a comment

Choose a reason for hiding this comment

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

Thinking more about it, I wonder if it wouldn't be simpler in terms of implementation to add a special "single namespace" lister/watcher. In other words, extend NewUnprivilegedNamespaceListWatchFromClient() to avoid the "get namespace" requests when a single namespace (not equal to all namespaces) is given.

Another thing to keep in mind: when running the operator in single namespace mode, the .spec.*NamespaceSelector fields become ineffective. When these selectors aren't nil, I wonder if the operator should report it via the "Reconciled=False" status configuration or if it should ignore the namespace selectors? I'd rather lean towards the first option as it's more explicit.

@zhuangzhi-cc
Copy link
Author

Thinking more about it, I wonder if it wouldn't be simpler in terms of implementation to add a special "single namespace" lister/watcher. In other words, extend NewUnprivilegedNamespaceListWatchFromClient() to avoid the "get namespace" requests when a single namespace (not equal to all namespaces) is given.

Another thing to keep in mind: when running the operator in single namespace mode, the .spec.*NamespaceSelector fields become ineffective. When these selectors aren't nil, I wonder if the operator should report it via the "Reconciled=False" status configuration or if it should ignore the namespace selectors? I'd rather lean towards the first option as it's more explicit.

Do you means create a fake ListerWatcher in NewUnprivilegedNamespaceListWatchFromClient like:

func NewUnprivilegedNamespaceListWatchFromClient(
ctx context.Context,
l log.Logger,
c cache.Getter,
allowedNamespaces, deniedNamespaces map[string]struct{},
fieldSelector fields.Selector,
) cache.ListerWatcher {
......
if IsAllNamespaces(allowedNamespaces) {
....
}

  if IsSingleNamespace(allowedNamespaces) {

	listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
		list := &v1.NamespaceList{}
		return list, nil
	}
	watchFunc := func(_ metav1.ListOptions) (watch.Interface, error) {
		// Since the client does not have Watch privileges, do not
		// actually watch anything. Use a watch.FakeWatcher here to
		// implement watch.Interface but not send any events.
		return watch.NewFake(), nil
	}
	return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
 }

.....
}

@simonpasquier
Copy link
Contributor

Yes something along those lines. It could even work when there are multiple namespaces in allowedNamespaces beccause the controllers only care about the namespace names after all.

func NewUnprivilegedNamespaceListWatchFromClient(
ctx context.Context,
l log.Logger,
c cache.Getter,
allowedNamespaces, deniedNamespaces map[string]struct{},
fieldSelector fields.Selector,
) cache.ListerWatcher {
...
if IsAllNamespaces(allowedNamespaces) {
   ....
}


listFunc := func(options metav1.ListOptions) (runtime.Object, error) {
                list := &v1.NamespaceList{}
                for name := range allowedNamespaces {
                        result := &v1.Namespace{}
                        list.Items = append(list.Items, &v1.Namespace{
                                ObjectMeta: metav1.ObjectMeta{Name: name},
                        })
                }
                return list, nil		
	})
}
watchFunc := func(_ metav1.ListOptions) (watch.Interface, error) {
		// Since the client does not have Watch privileges, do not
		// actually watch anything. Use a watch.FakeWatcher here to
		// implement watch.Interface but not send any events.
		return watch.NewFake(), nil
}

return &cache.ListWatch{ListFunc: listFunc, WatchFunc: watchFunc}
)

…s related logic create a fake watch list, and remove anyother change
@pull-request-size pull-request-size bot added size/M and removed size/XL labels Sep 5, 2023
@@ -521,7 +523,7 @@ func (c *Operator) Run(ctx context.Context) error {

c.addHandlers()

if c.kubeletSyncEnabled {
if c.kubeletSyncEnabled && !operator.IsPrometheusInSingleNamespace(c.config.Namespaces) {
Copy link
Contributor

Choose a reason for hiding this comment

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

still not sure why this change is required :)

Copy link
Author

Choose a reason for hiding this comment

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

It's my misunderstand, I have remove the related check.

Comment on lines 198 to 199
_, ok := ns[v1.NamespaceAll]
return !ok && len(ns) == 1
Copy link
Contributor

Choose a reason for hiding this comment

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

(nit)

Suggested change
_, ok := ns[v1.NamespaceAll]
return !ok && len(ns) == 1
_, allNamespaces := ns[v1.NamespaceAll]
return !allNamespaces && len(ns) == 1

Copy link
Author

Choose a reason for hiding this comment

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

this code has been removed.

@pull-request-size pull-request-size bot added size/S and removed size/M labels Sep 6, 2023
@simonpasquier
Copy link
Contributor

There's an issue with the testOperatorNSScope test (and more precisely the SingleNS subtest). The reason is that the namespace selection returns an empty list because the operator doesn't have access to the namespace's labels:

ruleNamespaceSelector, err := metav1.LabelSelectorAsSelector(p.Spec.RuleNamespaceSelector)
if err != nil {
return namespaces, errors.Wrap(err, "convert rule namespace label selector to selector")
}
namespaces, err = operator.ListMatchingNamespaces(ruleNamespaceSelector, c.nsMonInf)
if err != nil {
return nil, err
}

We have the same "problem" in other places:

// If 'ServiceMonitorNamespaceSelector' is nil only check own namespace.
if cpf.ServiceMonitorNamespaceSelector == nil {
namespaces = append(namespaces, objMeta.GetNamespace())
} else {
servMonNSSelector, err := metav1.LabelSelectorAsSelector(cpf.ServiceMonitorNamespaceSelector)
if err != nil {
return nil, err
}
namespaces, err = operator.ListMatchingNamespaces(servMonNSSelector, rs.namespaceInformers)
if err != nil {
return nil, err
}
}

This is similar to what I said in a previous comment

Another thing to keep in mind: when running the operator in single namespace mode, the .spec.*NamespaceSelector fields become ineffective. When these selectors aren't nil, I wonder if the operator should report it via the "Reconciled=False" status configuration or if it should ignore the namespace selectors? I'd rather lean towards the first option as it's more explicit.

In retrospect, the controllers should rather default the namespace selectors to nil and report the situation via the Reconciled status condition with a dedicated reason and message:

  availableReplicas: 1
  conditions:
  - lastTransitionTime: "2023-09-05T12:01:49Z"
    observedGeneration: 5
    status: "True"
    type: Available
  - lastTransitionTime: "2023-09-05T12:01:49Z"
    observedGeneration: 5
    status: "True"
    type: Reconciled
    reason: "NamespaceSelectorsIgnored"
    message: "The resource specifies namespace selectors for the ServiceMonitor, PrometheusRule custom resources but the operator can only select resources from the 'foo' namespace."

@pull-request-size pull-request-size bot added size/M and removed size/S labels Sep 12, 2023
@simonpasquier
Copy link
Contributor

@zhuangzhi-cc thanks for keeping up the PR up-to-date! I'd advise to wait for #5898 which fixes a problem that's a bit different but after it (and with the refactoring), it should be even easier to implement single-namespace watcher.

@simonpasquier
Copy link
Contributor

@zhuangzhi-cc now that #5898 and #5934 have merged, it's possible to run the operator watching a single namespace without ClusterRoleBinding. If you create a role binding to the prometheus-operator cluster role in the required namespace, it should just work. Does it satisfy your use case?

kc create rolebinding -n my_namespace prometheus-operator --clusterrole prometheus-operator --serviceaccount default:prometheus-operator
./operator --namespaces my_namespace --as system:serviceaccount:default:prometheus-operator

@zhuangzhi-cc
Copy link
Author

@zhuangzhi-cc thanks for keeping up the PR up-to-date! I'd advise to wait for #5898 which fixes a problem that's a bit different but after it (and with the refactoring), it should be even easier to implement single-namespace watcher.

@simonpasquier I see that PR #5898 has alreay merged, is there any idea about single-namespace feature?

@simonpasquier
Copy link
Contributor

@simonpasquier I see that PR #5898 has alreay merged, is there any idea about single-namespace feature?

@zhuangzhi-cc please check my comment right before (#5807 (comment)).

@zhuangzhi-cc
Copy link
Author

@simonpasquier I see that PR #5898 has alreay merged, is there any idea about single-namespace feature?

@zhuangzhi-cc please check my comment right before (#5807 (comment)).

Sorry for my browser not sync the latest message because I am not connect to VPN :), I would make some test with the latest code. thanks a lot!

@zhuangzhi-cc
Copy link
Author

@simonpasquier I see that PR #5898 has alreay merged, is there any idea about single-namespace feature?

@zhuangzhi-cc please check my comment right before (#5807 (comment)).

Sorry for my browser not sync the latest message because I am not connected to VPN :), I would make some test with the latest code. thanks a lot!

@zhuangzhi-cc
Copy link
Author

this feature has supported at #5898 and #5934

@simonpasquier
Copy link
Contributor

Thanks for confirming!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants