Cluster-api-provider-azure: Clean up ./cloud/services

Created on 16 Oct 2020  ยท  21Comments  ยท  Source: kubernetes-sigs/cluster-api-provider-azure

/kind cleanup

Describe the solution you'd like
A while back, the code used to interact with Azure under ./cloud/services was interfaced to allow some separation of concern between the controller / scopes and the types from the Azure SDK. With the advent of the azure.Service interface, Azure SDK / client code doesn't leak into the controllers. In fact, there should not longer be dependencies on the code that directly interacts with Azure. We should use this as an opportunity to crystallize this design goal of refactoring to azure.Service by cleaning up the service internals.

Current structure:

cloud/services/scalesets/
โ”œโ”€โ”€ client.go
โ”œโ”€โ”€ mock_scalesets
โ”‚ย ย  โ”œโ”€โ”€ client_mock.go
โ”‚ย ย  โ”œโ”€โ”€ doc.go
โ”‚ย ย  โ””โ”€โ”€ scalesets_mock.go
โ”œโ”€โ”€ service.go
โ”œโ”€โ”€ vmss.go
โ””โ”€โ”€ vmss_test.go

Proposed structure:

cloud/services/scalesets/
โ”œโ”€โ”€ client.go  <-- don't export this; make it private
โ”œโ”€โ”€ mock_scalesets
โ”‚   โ”œโ”€โ”€ client_mock.go
โ”‚   โ”œโ”€โ”€ doc.go
โ”‚   โ””โ”€โ”€ scaleset_mock.go
โ”œโ”€โ”€ scaleset.go <-- includes service and scope
โ””โ”€โ”€ scaleset_test.go

The only thing exported from the new structure is the Service struct and the Scope interface for each service. This will ensure that the encapsulation will not be violated and guide future contributors.

help wanted kincleanup

Most helpful comment

Great questions, @shysank.

  1. I generally refrain from returning interfaces, but in this situation, it might be a good idea to ensure that a consumer of the API can only expect an azure.Service.
  2. I think this will include a lot of work as you would then need to introduce disks.NewReconciler() azure.Reconciler and disks.NewDeleter azure.Deleter. I haven't thought through all of the implications of this change. I lean toward adding the no-op for now, returning an azure.Service, and possibly revisit in the future.

@CecileRobertMichon & @nader-ziada wdyt about this?

All 21 comments

/help

@CecileRobertMichon:
This request has been marked as needing help from a contributor.

Please ensure the request meets the requirements listed here.

If this request no longer meets these requirements, the label can be removed
by commenting with the /remove-help command.

In response to this:

/help

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.

/assign

@shysank please don't hesitate to ping me if you have any questions. Might be best to refactor one service in a PR before refactoring all at the same time.

any thoughts on how to replace code like https://github.com/kubernetes-sigs/cluster-api-provider-azure/blob/e92bba9b95d18578e09a4eaad1c1150c0c6f5516/exp/controllers/azuremangedcontrolplane_reconciler.go#L219-L263 ?

notably, I don't see many other reconcilers which write data back into the CRD?

that blurb is long -- referencing managedClustersSvc.Get and managedClustersSvc.GetCredentials to reconcile endpoint/kubeconfig

Ahh... the code for *MachinePool and the rest in ./exp have not been refactored to the use the scope pattern. See https://github.com/kubernetes-sigs/cluster-api-provider-azure/issues/757.

I would focus on a service not used by the ./exp packages.

I was thinking about how to refactor them in line with the same pattern (or if we should avoid that?) seems like the above solution would be one approach

We should refactor them. I've been remiss in not getting to them.

@devigned I'm trying to refactor disks service, and have a couple of questions:

  1. Should we also make service struct private, and only export the scope interface, and the service initialization function?
  2. Should we divide azure.Service into azure.Reconciler, and azure.Deleter to avoid the no-op in disks.Reconcile?

Great questions, @shysank.

  1. I generally refrain from returning interfaces, but in this situation, it might be a good idea to ensure that a consumer of the API can only expect an azure.Service.
  2. I think this will include a lot of work as you would then need to introduce disks.NewReconciler() azure.Reconciler and disks.NewDeleter azure.Deleter. I haven't thought through all of the implications of this change. I lean toward adding the no-op for now, returning an azure.Service, and possibly revisit in the future.

@CecileRobertMichon & @nader-ziada wdyt about this?

Great questions, @shysank.

  1. I generally refrain from returning interfaces, but in this situation, it might be a good idea to ensure that a consumer of the API can only expect an azure.Service.
  2. I think this will include a lot of work as you would then need to introduce disks.NewReconciler() azure.Reconciler and disks.NewDeleter azure.Deleter. I haven't thought through all of the implications of this change. I lean toward adding the no-op for now, returning an azure.Service, and possibly revisit in the future.

@CecileRobertMichon & @nader-ziada wdyt about this?

what is the reason to return an interface? its usually not a best practice and I worry we might be complicating things by adding extra abstractions

what is the reason to return an interface? its usually not a best practice and I worry we might be complicating things by adding extra abstractions

I guess my main concern was to avoid code like this disks.Service{} for initialization, and only use the exported api's. But, I agree that it's not a good idea to return an interface, although in this case, we will still return the actual type except that it can only be inferred.

I was recommending an interface since at some point in the future, we will likely replace the cloud code to use Azure Service Operator to reconcile Azure resources rather than the cloud services directly.

As long as Reconcile and Delete are the only funcs exported, I'm ๐Ÿ’ฏ behind return in a struct rather than an interface.

we will likely replace the cloud code to use Azure Service Operator to reconcile Azure resources rather than the cloud services directly

Just a thought, I'm noticing several places where exporting Reconcile/Delete and handing that off to another implementer is not really sufficient. There are several places where the services write back into crds, several of which have bugs or awkward ergonomics right now. I would not expect ASO or another project to implement some of these things as part of reconcile a resource, since they are CAPI/CAPZ specific.

examples:

other random opportunities for cleanup:

we still need to refactor the other services

/reopen

@nader-ziada: Reopened this issue.

In response to this:

we still need to refactor the other services

/reopen

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.

@shysank are you planning on working on the other services as well?

@nader-ziada Yeah, I'll take on the rest. Just caught up on some pending work in capi prs. Will start working on this once that is done.

/assign

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nader-ziada picture nader-ziada  ยท  8Comments

mboersma picture mboersma  ยท  6Comments

alexeldeib picture alexeldeib  ยท  9Comments

CecileRobertMichon picture CecileRobertMichon  ยท  5Comments

CecileRobertMichon picture CecileRobertMichon  ยท  5Comments