-
Notifications
You must be signed in to change notification settings - Fork 743
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
Mutation Objects #916
Mutation Objects #916
Conversation
f04f608
to
987e34d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking good! I left a few comments, though I realize this is WIP.
type Parameters struct { | ||
PathTests []PathTests `json:"pathTests,omitempty"` | ||
// IfIn Only mutate if the current value is in the supplied list | ||
IfIn []string `json:"ifIn,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want this to be a list of interface{}
?
That would allow for testing more complex objects as well as unknown types (string vs integer etc.)
That being said, coercing the types is much less ambiguous, and numbers might need other comparators anyway (greaterThan, for example).
Personally I'm split. Starting with string seems safer, as we can always expand the set of valid types over time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd go with the safer path for starters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
// IfNotIn Only mutate if the current value is NOT in the supplied list | ||
IfNotIn []string `json:"ifNotIn,omitempty"` | ||
// Value to assign | ||
Value string `json:"value,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Value needs to be of type interface{}
so users can assign arbitrary values to arbitrary points in an object's schema.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed Value type to runtime.RawExtension if that answers the way we add objects by definition
(both on assign_types.go
and assignmetadata_types.go
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also - interface{} does not seem to be supported by controller-gen
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can use RawExtension
as it looks like it expects to be wrapping an object:
https://godoc.org/k8s.io/apimachinery/pkg/runtime#RawExtension
How does controller-gen break if we try to use interface{}?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I played around a bit, it looks like json.RawMessage
might work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does controller-gen break if we try to use interface{}?
See kubernetes-sigs/controller-tools#294 (comment)
The suggestion there is indeed use either json.RawMessage
or RawExtension
.
@fedepaol @mmirecki have you encounter an issue on the POC or current PRs with processing one of these types ? can RawExtension
option to assign a full fledged sub-object meet our needs ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to be able to pass both a full fledged object (i.e. for the sidecar injection example) or a scalar value (i.e. when changing the image pull policy).
This drives two different things:
- What to put in the CRD manifest
- How to map it in golang
Here we want a field that can be both an object, a scalar value (bool, integer, etc) or an array.
If we declare the field to be an object in the CRD, a string won't be accepted: Invalid value: "string": spec.foo in body must be of type object: "string"
.
The option we have (I believe) is to use something like anyOf
for alternative typed properties, or go with the object way and expect it to have a fixed property like "value: foo" in case the path we are aiming at is not an object (but this last one feels clunky).
The way we declare the CRD drives the go struct, having a single field to be either a json or a rawextensions (both would work), or a serie of typed alternative fields (with one of them being an object).
@maxsmythe @yanirq thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think there's a way to use anyOf
in controller-gen, which is unfortunate. The sub-object route seems like it would work.
What if we changed spec.parameters.value
to be spec.parameters.assign.value
We could then have something like:
Parameters struct {
// +kubebuilder:validation:XPreserveUnknownFields
Assign runtime.RawExtension `json:"assign,omitempty"`
}
It's a bit hacky to use RawExtension as this isn't a K8s object, but it saves us wrapping json.RawMessage in a struct and writing custom serializers like they did for runtime.RawExtension.
Fun finding: controller-runtime infers schema type from field type, so json.RawMessage is typed as a string (it is an alias for []byte
), but runtime.RawExtension is an object because it is a struct enclosing a string.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxsmythe I changed the struct as suggested above.
One thought I had is to somehow override RawExtension.object to RawExtension.value just for semantic purposes (to get spec.parameters.assign.value
) I don't think there is such an option but I won't mind to be enlightened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could create your own "RawExtension", though switching .Object
to .Value
wouldn't work.
You could follow the same pattern raw extension does (create your own struct that implements MarshalJSON() and UnmarshalJSON(), use json:"-", and then extend it how you want, including lazy-unmarshaling the JSON when a .Value()
method is called.
} | ||
|
||
// AssignMetadataStatus defines the observed state of AssignMetadata | ||
type AssignMetadataStatus struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need pod-specific status reporting ad some point, though that can be added on as a follow-on PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow-on PR sounds good.
I'll make sure to open an issue if need be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 would be good to have an issue for this so we don't forget. Won't be necessary while we're just getting things running, but very necessary for users.
} | ||
|
||
type MetadataParameters struct { | ||
Value string `json:"value,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
String makes sense here as annotations and labels can only be strings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So something should be added/changed here ? or keep it as it is ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this works as-is :)
- apiGroups: | ||
- mutations.gatekeeper.sh | ||
resources: | ||
- assignmetadata |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why one resource is "assigns" (plural) and the other is "assignmetadata" (singular)
Also, I don't see the CRD manifests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assignmetadata now changed to plural
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome, thanks!
Codecov Report
@@ Coverage Diff @@
## master #916 +/- ##
=======================================
Coverage 43.46% 43.46%
=======================================
Files 47 47
Lines 3173 3173
=======================================
Hits 1379 1379
Misses 1598 1598
Partials 196 196
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report at Codecov.
|
apis/mutations/v1alpha1/match.go
Outdated
// APIGroups is the API groups the resources belong to. '*' is all groups. | ||
// If '*' is present, the length of the slice must be one. | ||
// Required. | ||
APIGroups string `json:"apiGroups,omitempty" protobuf:"bytes,1,rep,name=apiGroups"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think both this and the next one must be []string to be aligned with Constraints.
@maxsmythe can you confirm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you are correct, both of these should be []string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
@maxsmythe I covered all the comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -140,6 +140,9 @@ run: generate manifests | |||
install: manifests | |||
kustomize build config/crd | kubectl apply -f - | |||
|
|||
deploy-mutation: deploy | |||
kustomize build --load_restrictor LoadRestrictionsNone config/overlays/mutation | kubectl apply -f - |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a big fan of removing load restrictions, but it seems like this is probably the cleanest way of having dev CRDs, otherwise we'd need to change the file structure a bit, which may not play well with controller-gen.
TL;DR this works
IfIn []string `json:"ifIn,omitempty"` | ||
// IfNotIn Only mutate if the current value is NOT in the supplied list | ||
IfNotIn []string `json:"ifNotIn,omitempty"` | ||
// Assign holds the value to be assigned |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Assign.value holds the value to be assigned
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
names: | ||
kind: Assign | ||
listKind: AssignList | ||
plural: assigns |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the plural just be "assign"?
I'm a bit biased here as I really don't like K8s use of plurals, I think having multiple names causes more problems than it solves.
In any case whatever we choose should be consistent with assignmetadata
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed plurals of assign
and assignmetadata
to be consistent (singular names).
Fun fact: controller-gen (flect) perceives assignmetadata
plural as assignmetadata
probably due to the ata
suffix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting. Programatically managing these grammar details seems like a hard thing to do.
@@ -0,0 +1,24 @@ | |||
# permissions for end users to edit assignmetadatas. | |||
apiVersion: rbac.authorization.k8s.io/v1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
None of these roles are necessary, they should all be folded up into the gatekeeper-manager role
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
# The following patch adds a directive for certmanager to inject CA into the CRD | ||
# CRD conversion requires k8s 1.13 or later. | ||
apiVersion: apiextensions.k8s.io/v1beta1 | ||
kind: CustomResourceDefinition |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did we remove permissions entirely?
I thought we were going to add them to the gatekeeper-manager role?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After digging deeper I learned that there is a convention where controller-gen generates these permissions and outputs them to role.yaml according to kubebuilder markers on the controllers themselves (and not on CRDs) as we can see in config and constraints for example.
I can add them manually to gatekeeper-manager role (which will be overridden and exclude these permission when we run deploy) or we can add them to the role.yaml file in the controller related PRs (I already added a comment for one here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maxsmythe was the permissions comment/question placed here on purpose ? (cainjection_in_assign.yaml) I'm trying to understand, out of placement context, if I missed anything else.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just placed randomly, I probably should have just added it to the bottom of the PR. Sorry.
You are correct. I'd forgotten that we were putting the permissions on the controller (which makes sense as that's the thing that actually needs the access).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@maxsmythe PR re-based to resolve conflicts and allow merge. |
@@ -17,13 +17,17 @@ patchesStrategicMerge: | |||
#- patches/webhook_in_configs.yaml | |||
#- patches/webhook_in_constraintpodstatuses.yaml | |||
#- patches/webhook_in_constrainttemplatepodstatuses.yaml | |||
#- patches/webhook_in_assignmetadata.yaml |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we want to add a TODO here to make sure we add the following under resources and remove the one under overlays in the future?
- bases/mutations.gatekeeper.sh_assignmetadata.yaml
- bases/mutations.gatekeeper.sh_assign.yaml
resources: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
Just one nit
6a4782e
to
8070c95
Compare
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
Signed-off-by: Yanir Quinn <yquinn@redhat.com>
8070c95
to
97bfc5c
Compare
What this PR does / why we need it:
Provided CRDs to support mutation: Assign and AssignMetadata CRDs
The PR includes CRDs + generated artifacts.
Which issue(s) this PR fixes (optional, using
fixes #<issue number>(, fixes #<issue_number>, ...)
format, will close the issue(s) when the PR gets merged):Fixes #907
Special notes for your reviewer:
deploy-mutation might be necessary in this PR to deploy mutation specific resources (using an additional overlay to kustomize)