-
Notifications
You must be signed in to change notification settings - Fork 277
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
StatefulSet Override #175
StatefulSet Override #175
Conversation
- new crd properties: spec.override.statefulSet, which is our own definition of appsv1.StatefulSet, which allows users to provide a patch that will be applied on top of generated statefulset definition as an override. - new type EmbeddedObjectMeta, which is a partially metav1.ObjectMeta; it is used in StatefulSet, PodTemplateSpec, and PersistentVolumeClaim. - custome types are needed because of the pruning behavior of `preserveUnknownFields: false`, which is going to be the default behavior for crd v1. - all fields in the custom StatefulSetSpec are optional; including selector, serviceName...etc
Spec: &rabbitmqv1beta1.StatefulSetSpec{ | ||
VolumeClaimTemplates: []rabbitmqv1beta1.PersistentVolumeClaim{ | ||
{ | ||
EmbeddedObjectMeta: rabbitmqv1beta1.EmbeddedObjectMeta{ |
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.
Would it compile if (as part of the test) you built this as a native StatefulSet, marshalled it as JSON, then unmarshalled it as our custom 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.
The reason I ask is because it would be a much better developer experience for this to be opaque to the user. Obviously document that only certain fields in object meta are available. But I think it's a tough sell if they have to build a different struct than the native resource
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 it will compile James! I didn't look into this because I liked the flexibility of not having any required field in StatefulSetSpec with the current implementation. If I were to build and also patch as the native StatefulSetSpec, all the required fields will stay the same as k8s sts and people will need to specific things like serviceName
, and selectors
all the time again.
What I like:
One limitations I see:
|
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's looking good. I haven't manually tested this in Kind yet. I left a few comments; the main thing that poped out was the implementation of label/annotation override.
It("overrides statefulSet.spec.selector", func() { | ||
instance.Spec.Override.StatefulSet = &rabbitmqv1beta1.StatefulSet{ | ||
Spec: &rabbitmqv1beta1.StatefulSetSpec{ | ||
Selector: &metav1.LabelSelector{ | ||
MatchLabels: map[string]string{ | ||
"my-label": "my-label", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
stsBuilder := cluster.StatefulSet() | ||
obj, err := stsBuilder.Build() |
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 we should make changes in the instance of the "resource builder" builder i.e. cluster.instance
; same as we do in internal/resource/statefulset_test.go#L312
, in the Update
context. The end result will be the same because instance
is a pointer used in cluster
, but I think it reads better if we modify cluster.instance
.
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.
Would you mind if I address that as a separate PR? I think you have a valid point here, but the rest of the Build
test specs are following the same pattern. I would like to have a separate PR to address all of these.
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.
Sure, make it part of another refactoring PR 👌
Resources: corev1.ResourceRequirements{ | ||
Requests: corev1.ResourceList{ | ||
corev1.ResourceStorage: *instance.Spec.Persistence.Storage, | ||
}, | ||
}, | ||
StorageClassName: &storageClass, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
cluster = &resource.RabbitmqResourceBuilder{ | ||
Instance: &instance, | ||
Scheme: scheme, | ||
} | ||
stsBuilder := cluster.StatefulSet() |
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.
Regenerating RabbitmqResourceBuilder
doesn't feel right because the BeforeEach
at Describe
level is doing this for us. I believe we should modify cluster.instance
with any properties we want to assert on, then call Build()
, instead of regenerating the RabbitmqResourceBuilder
entirely. Same as we do in Update
context further down.
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.
Would you mind if I address that as a separate PR together with the previous comment you made? Again, the rest of the Build
test specs are following the same pattern. I would like to have a separate PR to address all of these.
The limitation is the "desired behavior" of PVC, because in the type |
It("overrides statefulSet.spec.selector", func() { | ||
instance.Spec.Override.StatefulSet = &rabbitmqv1beta1.StatefulSet{ | ||
Spec: &rabbitmqv1beta1.StatefulSetSpec{ | ||
Selector: &metav1.LabelSelector{ | ||
MatchLabels: map[string]string{ | ||
"my-label": "my-label", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
stsBuilder := cluster.StatefulSet() | ||
obj, err := stsBuilder.Build() |
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.
Sure, make it part of another refactoring 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.
All looks good to me
- this is related and a followup to feature in PR #175 - SetControllerReference should be called after override is applied since the object's name and namespace could be modified by the override. - delete the unit test which asserts that sts's ns can be overwritten by override; the test is incorrect and will never pass, since cluster-scoped resources (sts in this case), can not be owned by objects in a other namespace.
- this is related to PR #175 - ObjectMeta.Labels and ObjectMeta.Annotations are merged when patching instead of replace.
- this is related and a followup to feature in PR #175 - SetControllerReference should be called after override is applied since the object's name and namespace could be modified by the override. - delete the unit test which asserts that sts's ns can be overwritten by override; the test is incorrect and will never pass, since cluster-scoped resources (sts in this case), can not be owned by objects in a other namespace.
- this is related to PR #175 - ObjectMeta.Labels and ObjectMeta.Annotations are merged when patching instead of replace.
This closes #143
Commits will be squashed when merging
Summary Of Changes
Added new types:
EmbeddedObjectMeta
,StatefulSetSpec
,PodTemplateSpec
, andPersistentVolumeClaim
Context
So with preserved-unknown-fields set to false, schema validation will prune objectMeta since it doesn't have any properties associate with it. This pruning behavior is due to
preserveUnknownFields: false
, which is going to be the default behavior for crd v1. There is a stale kubebuilder issue which proposes to skip pruning on embedded objectMeta, but due to its inactivity, we cannot rely on the change. The same problem is described in this kubebuilder issue.Name
,Namespace
,Labels
, andAnnotations
, I think it should be enough for most use cases.serviceName
, orselector
to the sts spec when they try to modify other fields.Testing
This feature is tested in unit, and integration tests. I decided not to add a system tests for it because verifying that the generated StatefulSet definition is correct is enough.
Required minimal version
We are using k8s api version 1.17 which means that k8s api object, such as
corev1.PodTemplate
has a new set of golang marker defined that is only added at version 1.16 and 1.17 for server side apply functionality. With this change, our CRD will longer deploy to a 1.15 cluster because k8s api does not recognize these new Golang markers.We will need to document required version, as well as upgrade CI clusters that we are using.
Cannot be installed on k8s 1.18
why
issue in controller-tool which talks about the same issue
Example
To try this feature by hand, here is a rabbitmqcluster manifest which adds a new PVC, a mount, and uses override to overwrite the replicas count.