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

Option to customize NamespaceTransformer role binding subject handling #4704

Merged
merged 1 commit into from Jul 28, 2022

Conversation

KnVerey
Copy link
Contributor

@KnVerey KnVerey commented Jul 11, 2022

Resolves #1599
Closes #4301 and #4302, which are alternative solutions to the same issue.

We can't change the default, since that would be enormously disruptive to our users. Instead, this PR proposes a new option for the transformer to enable it to behave in the desired manner. This option are exposed explicitly via the transformer's configuration resource, which can be used in the transformers field. While using resource-specific annotations to configure transformers is an interesting idea that may be appropriate in some cases, my opinion is that transformer-level is better granularity for these particular options. I also considered a selection-base solution as suggested here, but at that point I think folks should reach for replacements instead, now that they exist.

apiVersion: builtin
kind: NamespaceTransformer
metadata:
  name: notImportantHere
  namespace: test
setRoleBindingSubjects: defaultOnly|allServiceAccounts|none # new
fieldSpecs:
- path: metadata/name
  kind: Namespace
  create: true

Originally, this PR had all instead of allServiceAccounts. However, the other two documented kinds are not namespaced, so setting all of them / using fieldspecs for these will never make sense. I'm not sure from the docs whether it is possible to have auth plugins with their own bespoke kinds. If that is possible, and those kinds are namespaced, they will not be handled by this transformer.

@k8s-ci-robot
Copy link
Contributor

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jul 11, 2022
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Jul 11, 2022
// objects will have their namespace fields set. Overrides field specs provided for these types, if any.
// - defaultOnly (default): namespace will be set only on subjects named "default".
// - all: namespace will be set on all subjects
// - none: all subjects will be skipped.
Copy link
Contributor

Choose a reason for hiding this comment

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

This is an interesting idea, and I really like it!

Copy link
Contributor Author

@KnVerey KnVerey Jul 11, 2022

Choose a reason for hiding this comment

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

One complication I just realized is the meaning of "all". Some options:

  1. Support the value, and drive it with create: false fieldspecs. This means all subject namespaces with any value will be overwritten, allowing the user to effectively tell us which subjects are namespaced by setting dummy values in them. Has a weird interaction where skipExisting: true, setRoleBindingSubjects: all sets nothing.
  2. Support the value, and drive it with create: true fieldspecs. I think this is unacceptable, because it will add namespaces to subjects with cluster-scoped kinds.
  3. Don't support that value, but stop stripping the role binding fieldspecs so that users can do one of the above themselves (not any more elegantly).
  4. Don't support that value, and continue stripping the role binding fieldspecs.
  5. Support the value, and drive it with openAPI-aware and/or bespoke code (I could be wrong don't think User and Group are part of openapi, since they're not real kinds). Much the same as we do for metadata.namespace, except only do it if the "all" option is specified or the fieldspecs are given.

This PR currently does the first one. But I'm leaning towards one of the last two after typing this out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I update the PR to do option 5. Confirmed that the OpenAPI does not identify User or Group as cluster-scoped, so was not usable for this purpose.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for the late response. I agree that the last two options are best, and am happy with the choice of option 5.

@k8s-ci-robot
Copy link
Contributor

@KnVerey: This PR has multiple commits, and the default merge method is: merge.
You can request commits to be squashed using the label: tide/merge-method-squash

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@KnVerey KnVerey force-pushed the ns_transformer_options branch 2 times, most recently from 6375d3b to feb93be Compare July 11, 2022 23:34
func SetEntry(key, value, tag string) SetFn {
// SetEntry returns a SetFn to set a field or a map entry to a value.
// It can be used with an empty name to set both a value and a tag on a scalar node.
// When setting only a value on a scalar node, use SetScalar instead.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These new sentences were already true, but since the comment said it "set an entry in a map" I was initially confused, thinking NamespaceTransformer was doing the wrong thing / relying on an unintentional behaviour. But no, FieldSetter is documented to handle both scalars and maps, so SetEntry does too (even though "entry" sounds like it targets a sequence, which is not handled 😕 ).

.golangci.yml Outdated Show resolved Hide resolved
Makefile-tools.mk Outdated Show resolved Hide resolved
return func(node *yaml.RNode) error {
return node.PipeE(yaml.FieldSetter{StringValue: value})
}
return SetEntry("", value, yaml.NodeTagEmpty)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since yaml.FieldSetter with an empty Name and populated StringValue creates a NewScalarRNode internally, this is exactly the same thing.

n := &yaml.Node{
Kind: yaml.ScalarNode,
Value: value,
Tag: tag,
}
return func(node *yaml.RNode) error {
return node.PipeE(yaml.FieldSetter{
Name: key,
Name: name,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

as with the other changes, this is to make it more clear that it's perfectly fine to use SetEntry with scalars (which don't have keys)

kind: RoleBinding
- path: subjects
- path: subjects/namespace
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These were incorrect and previously unused. While subjects is a valid fieldspec, it returns a map that includes the namespace field. The name reference transformer uses specs like as a signal to modify multiple fields at once (name and namespace), but this one expects scalars. I take it the pre-kyaml version of the transformer used these, and it must have been written differently.

Copy link
Contributor

Choose a reason for hiding this comment

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

The subjects is a slice of object which can have namespace field. Using subjects/namespace here seems to indicate kustomize deals with slice type in jsonpath this way (compared to the replacement transformer syntax [], like subjects[].namespace).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Correct, it isn't JsonPath syntax. For better or for worse, these older transformers use Kustomize's own field path syntax in their fieldSpec fields.

@@ -6,14 +6,12 @@ package builtinpluginconsts
const (
namespaceFieldSpecs = `
namespace:
- path: metadata/namespace
create: true
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We could leave this in, or not. It's ignored either way... in fact it is actively stripped out when the defaults or user-provided specs include it.

api/krusty/namedspacedserviceaccounts_test.go Outdated Show resolved Hide resolved
api/filters/namespace/namespace.go Outdated Show resolved Hide resolved
// objects will have their namespace fields set. Overrides field specs provided for these types, if any.
// - defaultOnly (default): namespace will be set only on subjects named "default".
// - all: namespace will be set on all subjects
// - none: all subjects will be skipped.
Copy link
Contributor Author

@KnVerey KnVerey Jul 11, 2022

Choose a reason for hiding this comment

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

One complication I just realized is the meaning of "all". Some options:

  1. Support the value, and drive it with create: false fieldspecs. This means all subject namespaces with any value will be overwritten, allowing the user to effectively tell us which subjects are namespaced by setting dummy values in them. Has a weird interaction where skipExisting: true, setRoleBindingSubjects: all sets nothing.
  2. Support the value, and drive it with create: true fieldspecs. I think this is unacceptable, because it will add namespaces to subjects with cluster-scoped kinds.
  3. Don't support that value, but stop stripping the role binding fieldspecs so that users can do one of the above themselves (not any more elegantly).
  4. Don't support that value, and continue stripping the role binding fieldspecs.
  5. Support the value, and drive it with openAPI-aware and/or bespoke code (I could be wrong don't think User and Group are part of openapi, since they're not real kinds). Much the same as we do for metadata.namespace, except only do it if the "all" option is specified or the fieldspecs are given.

This PR currently does the first one. But I'm leaning towards one of the last two after typing this out.

@KnVerey
Copy link
Contributor Author

KnVerey commented Jul 12, 2022

/test all

@KnVerey KnVerey changed the title Options to customize NamespaceTransformer overwriting and role binding subject handling Option to customize NamespaceTransformer role binding subject handling Jul 12, 2022
@k8s-ci-robot k8s-ci-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Jul 15, 2022
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels Jul 15, 2022
- path: metadata/name
kind: Namespace
create: true
- path: subjects
- path: subjects/namespace
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These are also stripped. Should we fix them (as this is doing) or just remove them?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is there any purpose of keeping them in here? I see why we need to strip them for user provided fieldspecs but I'm not sure I understand the purpose of keeping stripped values in our builtin fieldspecs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It would just be to show that the transformer affects these fields, I guess. In theory we could use it as a signal of whether or not we should (manually) modify them, but in practice that could be a breaking change for people who have existing manually configured transformers. I'm definitely fine with removing them.

Copy link
Contributor

Choose a reason for hiding this comment

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

I have a slight preference for removing them, but don't feel too strongly either way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed

group: rbac.authorization.k8s.io
- path: subjects/namespace
kind: ClusterRoleBinding
group: rbac.authorization.k8s.io
Copy link
Contributor Author

Choose a reason for hiding this comment

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

in the tests, I'm keeping both the old invalid paths and the valid paths to prove they all get stripped. If we stop stripping the old invalid ones, that could cause an error for someone who has customized their transformer by copying the default specs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we emit a warning if we see an old invalid fieldspec? I imagine we'll eventually want to remove the code that handles the legacy fieldspecs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can, but another option is to improve the error message for all cases where we're given a path that leads to a non-sequence node, and allow this case to fall through to that (hopefully helpful) error message. Plus make sure to have a release note stating that we fixed a bug where certain invalid fieldspecs were being ignored in the advanced configuration of the namespace transformer. #4727 takes that approach. It includes a dep change, so is a bigger risk for the kubectl update. I suggest we should merge this one (which continues to ignore the invalid fieldspec) and then make a decision on the invalid fieldspec in the other PR.

@KnVerey KnVerey marked this pull request as ready for review July 15, 2022 20:10
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 15, 2022
Copy link
Contributor

@natasha41575 natasha41575 left a comment

Choose a reason for hiding this comment

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

Apart from removing the unnecessary fieldspecs and a couple other minor comments, this LGTM

case namespace.SubjectModeUnspecified:
p.SetRoleBindingSubjects = namespace.DefaultSubjectsOnly
default:
return errors.Errorf("invalid value %s for setRoleBindingSubjects", p.SetRoleBindingSubjects)
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: suggest listing the valid values here to help users fix the issue (and quoting the values)

i.e. errors.Errorf("invalid value %q for setRoleBindingSubjects, must be one of %q or %q", p.SetRoleBindingSubjects, namespace.AllServiceAccountSubjects, namespace.DefaultSubjectsOnly)

@natasha41575
Copy link
Contributor

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Jul 28, 2022
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: KnVerey, natasha41575

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:
  • OWNERS [KnVerey,natasha41575]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
None yet
4 participants