Skip to content

ceph: implement controller-runtime for admission controllers#5761

Merged
travisn merged 1 commit intorook:masterfrom
vbnrh:rook-ac-controller-runtime
Jul 13, 2020
Merged

ceph: implement controller-runtime for admission controllers#5761
travisn merged 1 commit intorook:masterfrom
vbnrh:rook-ac-controller-runtime

Conversation

@vbnrh
Copy link
Contributor

@vbnrh vbnrh commented Jul 6, 2020

moves the admission-controller server to use controller-runtime library for better support, maintainibility of code.

Signed-off-by: Vineet Badrinath vbadrina@redhat.com

Description of your changes:
This is a follow-up PR for #5045
It moves the server logic to use controller-runtime library for webhooks.

This help in better support, maintenance and performance of the code.

tlsCertFile = "tls.crt"
tlsKeyFile = "tls.key"
serverPort int32 = 8079
port = 8079
Copy link
Member

Choose a reason for hiding this comment

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

What is the difference between serverPort and port? Can't we just re-use serverPort?

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 ! Moved the dir and port to cmd/ceph/admission.go

logger.Infof("starting the webhook for backend %q", a.providerName)
certPath := filepath.Join(tlsDir, tlsCertFile)
keyPath := filepath.Join(tlsDir, tlsKeyFile)
keyPair, err := NewTlsKeypairReloader(certPath, keyPath)
Copy link
Member

Choose a reason for hiding this comment

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

Are tlsutil.go and controller.go used anymore? Can we remove them? If so, then perhaps this package is so simple we can delete the whole package and move the remaining code under the ceph provider. For example, similar to what was done for the nfs operator in pkg/operator/nfs/webhook.go.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a draft PR. There are still some issues i am working on. I'll let you know once the updates are done

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've removed the tlsutil.go and controller.go

Copy link
Contributor Author

@vbnrh vbnrh Jul 7, 2020

Choose a reason for hiding this comment

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

@travisn the code from the package (server.go) is meant to be a generic server code that can work for all providers. I think it would make sense to move it under pkg/operator/admission/server.go. May add another file later in this package for the unit tests. WDYT ?

Copy link
Member

Choose a reason for hiding this comment

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

The generic code is so short now that I wonder if it makes sense to have any common code. The controller runtime packages already have the common code.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, i've moved the server code under pkg/operator/ceph/server.go

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch 5 times, most recently from 425b073 to 0e05481 Compare July 7, 2020 16:22
logger.Infof("starting the webhook for backend %q", a.providerName)
certPath := filepath.Join(tlsDir, tlsCertFile)
keyPath := filepath.Join(tlsDir, tlsKeyFile)
keyPair, err := NewTlsKeypairReloader(certPath, keyPath)
Copy link
Member

Choose a reason for hiding this comment

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

The generic code is so short now that I wonder if it makes sense to have any common code. The controller runtime packages already have the common code.

if err != nil {
logger.Errorf("failed to add to scheme %v. ", err)
}
a := admission.New("ceph", port, certDir, &cephv1.CephCluster{}, scheme)
Copy link
Member

Choose a reason for hiding this comment

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

If we want to implement the admission webhook for multiple CRDs, we would need to start them all here (in separate goroutines, right? I guess they would just each listen on a separate port.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Though i havent tested it yet but i think that may not be the right approach as adding new ports means adding more services + validatingwebhookconfigs+ adding each port on the deployment for each of the CRDs to expose the port.

Copy link
Contributor Author

@vbnrh vbnrh Jul 8, 2020

Choose a reason for hiding this comment

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

I have updated the code to add CephBlockPool validation. I've tried to make the code as generic and easy to change to add more CRDs. Here are the steps i did for cephblockpool which can be used for other ceph CRDs.

  1. Implement webhook.Validator interface in pkg/apis/ceph.rook.io/v1/webhook.go for the CRD which needs to be validated
  2. Add a ValidatingWebhookConfiguration (copy-paste work) in webhook-config.yaml changing the relevant fields ( name, path, resources) and make sure to note down the path given here.
  3. In cmd/ceph/admission.go, Add the above path and map it to the given CRD in pathResourceMap.
  4. Done ! Test it 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.

Similiar approach can be followed for other providers also.

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch 2 times, most recently from 76dc7b3 to 7010343 Compare July 8, 2020 14:20
}
scheme = runtime.NewScheme()
pathResourceMap = map[string]webhook.Validator{
"/validate-cephclusters": &cephv1.CephCluster{},
Copy link
Member

Choose a reason for hiding this comment

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

Let's define this pathResourceMap inside the pkg/operator/ceph/server.go file. I don't see a reason to have this in the cmd package.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, i'll move it here as it is part of ceph package. We may need to move it out once other providers implement their validations.

a := admission.New(context, "ceph", operator.ValidateCephResource)
err := cephv1.AddToScheme(scheme)
if err != nil {
logger.Errorf("failed to add to scheme %v. ", err)
Copy link
Member

Choose a reason for hiding this comment

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

If there is a failure we need to exit since the admission controller would fail to start later anyway. See other places where we call

rook.TerminateFatal(err)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,89 @@
package v1
Copy link
Member

Choose a reason for hiding this comment

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

How about separate source files for different CRDs? For example, validate_cluster.go and validate_pool.go

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

// will be registered for the validating webhook.
var _ webhook.Validator = &CephCluster{}

func (in *CephCluster) ValidateCreate() error {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of in, it's more common to use the first letter of the type.

Suggested change
func (in *CephCluster) ValidateCreate() error {
func (c *CephCluster) ValidateCreate() error {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed


var _ webhook.Validator = &CephBlockPool{}

func (in *CephBlockPool) ValidateCreate() error {
Copy link
Member

Choose a reason for hiding this comment

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

p for pool

Suggested change
func (in *CephBlockPool) ValidateCreate() error {
func (p *CephBlockPool) ValidateCreate() error {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

if in.Spec.Replicated.Size > 0 || in.Spec.Replicated.TargetSizeRatio > 0 {
return errors.New("invalid create: both erasurecoded and replicated fields cannot be set at the same time")
}
}
Copy link
Member

Choose a reason for hiding this comment

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

A couple other checks:

  • If either codingChunks or dataChunks are set, then dataChunks must be at least 2 and codingChunks must be at least 1.
  • If all of these values are 0, it's an error. You must set either replicated or erasure coded settings.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done !

}

// StartServer will start the server
func (a *AdmissionController) StartServer() {
Copy link
Member

Choose a reason for hiding this comment

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

StartServer() should return an err so that the cmd package can call TerminateFatal() if there is some issue starting the webhook.

Suggested change
func (a *AdmissionController) StartServer() {
func (a *AdmissionController) StartServer() error {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), opts)
if err != nil {
logger.Errorf("failed to create manager %v. ", err)
Copy link
Member

Choose a reason for hiding this comment

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

return the err

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

logger.Info("starting webhook server")
err = mgr.Start(ctrl.SetupSignalHandler())
if err != nil {
logger.Errorf("failed to start server %v. ", err)
Copy link
Member

Choose a reason for hiding this comment

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

return the err

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

- apiGroups: ["ceph.rook.io"]
apiVersions: ["v1"]
operations: ["CREATE","UPDATE","DELETE"]
resources: ["cephblockpools"]
Copy link
Member

Choose a reason for hiding this comment

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

Defining a separate ValidatingWebhookConfiguration for every CRD is really painful. Can you point to other examples of where this is done in other projects? I would really like to confirm if we can have one definition and watch all CRDs in the apiGroup ceph.rook.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.

I've updated the code. We can now make do with just one ValidatingWebhookConfiguration but we have to add a webhook in the array for each CRD and make sure that the path is written with the same template

Copy link
Member

Choose a reason for hiding this comment

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

Looks good since it is in the same webhook config and we can use a single endpoint.

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch 3 times, most recently from 42fd480 to 6beab17 Compare July 9, 2020 12:43
- apiGroups: ["ceph.rook.io"]
apiVersions: ["v1"]
operations: ["CREATE","UPDATE","DELETE"]
resources: ["cephblockpools"]
Copy link
Member

Choose a reason for hiding this comment

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

Looks good since it is in the same webhook config and we can use a single endpoint.

if err != nil {
rook.TerminateFatal(err)
}
a := operator.NewAdmissionController("ceph", port, certDir, scheme)
Copy link
Member

Choose a reason for hiding this comment

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

Do we even need to pass any of these parameters? What if we just define them directly inside StartServer()? It looks like they aren't configurable, so we don't need to expose them here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to server.go

func (a *AdmissionController) StartServer() error {
logger.Infof("starting the webhook for backend %q", a.providerName)

opts := ctrl.Options{
Copy link
Member

Choose a reason for hiding this comment

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

For example, related to my other comment, can we just initialize the scheme, port, and certDir here?

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've moved the inits to server.go

)

type AdmissionController struct {
providerName string
Copy link
Member

Choose a reason for hiding this comment

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

This code is in the ceph namespace now, so no need to make it generic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch from 6beab17 to 94930b3 Compare July 9, 2020 16:46
@vbnrh vbnrh changed the title WIP ceph: implement controller-runtime for admission controllers ceph: implement controller-runtime for admission controllers Jul 9, 2020

a := admission.New(context, "ceph", operator.ValidateCephResource)
a.StartServer()
err := operator.StartServer()
Copy link
Member

Choose a reason for hiding this comment

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

Since we don't have an admission controller struct anymore, let's rename the method.

Suggested change
err := operator.StartServer()
err := operator.StartAdmissionController()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch from 94930b3 to 2d8856f Compare July 9, 2020 16:52

func ValidatePoolSpecs(ps PoolSpec) error {
// Checks if either ErasureCoded or Replicated fields are set
if ps.ErasureCoded.CodingChunks <= 0 && ps.ErasureCoded.DataChunks <= 0 && ps.Replicated.TargetSizeRatio <= 0 && ps.Replicated.Size <= 0 {
Copy link
Member

Choose a reason for hiding this comment

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

We really should have unit tests for all of these validations. It should be fairly simple to create them, so would be great to have them with this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Working on it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated with unit tests. please review

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch 2 times, most recently from b2b8ade to 38f106a Compare July 13, 2020 12:56
err := yaml.Unmarshal(cephClusterYaml, c)
assert.Nil(t, err)
err = c.ValidateCreate()
assert.Nil(t, err)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of assert.Nil(), it's preferred to use assert.NoError()

Suggested change
assert.Nil(t, err)
assert.NoError(t, err)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

c.Spec.External.Enable = true
c.Spec.Monitoring = MonitoringSpec{}
err = c.ValidateCreate()
assert.NotNil(t, err)
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
assert.NotNil(t, err)
assert.Error(t, err)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

# key: value
`)
}
func getCephClusterCRD() []byte {
Copy link
Member

Choose a reason for hiding this comment

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

Instead of creating yaml and unmarshalling it in the test, let's just use the golang types directly. For example, you should be able to directly declare any of the structs in types.go, change the properties, and then validate them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch from 38f106a to 3c74f26 Compare July 13, 2020 18:39
}
}
func getCephClusterCRD() *CephCluster {
return &CephCluster{
Copy link
Member

Choose a reason for hiding this comment

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

Much better instead of yaml, thanks. One more point is that there is no need for the test to set all the fields. Just set what is needed for an individual test instead of all the optional fields. With that approach, you likely don't even need a helper to create the struct and you can just create a small struct in each individual test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well i was thinking about not adding any duplicates in code but I've removed the helper funcs.

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch from 3c74f26 to e88dfe4 Compare July 13, 2020 19:14
c := &CephCluster{
ObjectMeta: metav1.ObjectMeta{
Name: "rook-ceph",
Namespace: "rook-ceph",
Copy link
Member

Choose a reason for hiding this comment

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

You likely don't even need to set the Namespace for the test.

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. I've just kept the name and datadirhostpath

assert.NoError(t, err)
c.Spec.External.Enable = true
c.Spec.Monitoring = MonitoringSpec{
Enabled: false,
Copy link
Member

Choose a reason for hiding this comment

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

false is the default, no need to set it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oops, it was supposed to be true. I've fixed it

Namespace: "rook-ceph",
},
Spec: PoolSpec{
FailureDomain: "osd",
Copy link
Member

Choose a reason for hiding this comment

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

Validation doesn't do anything with the FailureDomain, right? No need to set it for the test.

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

@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch from e88dfe4 to 8e2c88d Compare July 13, 2020 19:39
moves the admission-controller server to use controller-runtime library for better support, maintainibility of code.

Signed-off-by: Vineet Badrinath <vbadrina@redhat.com>
@vbnrh vbnrh force-pushed the rook-ac-controller-runtime branch from 8e2c88d to 98874cc Compare July 13, 2020 19:40
Copy link
Member

@travisn travisn left a comment

Choose a reason for hiding this comment

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

The test changes look good thanks!

@travisn travisn merged commit 7dcd64f into rook:master Jul 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants