-
Notifications
You must be signed in to change notification settings - Fork 431
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
Use a list for Azure services in reconcilers #2146
Use a list for Azure services in reconcilers #2146
Conversation
tagsSvc: tags.New(scope), | ||
scope: scope, | ||
services: []azure.Reconciler{ | ||
groups.New(scope), |
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 the same order as the Reconcile() calls below
inboundnatrules.New(machineScope), | ||
networkinterfaces.New(machineScope, cache), | ||
availabilitysets.New(machineScope, cache), | ||
disks.New(machineScope), |
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 the same order as Reconcile() below except for disks that were not present before in Reconcile (it's a no-op) but needs to be deleted after the VM (hence needs to be before VMs in the list)
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.
What was our behavior before this change? Would the disk get deleted anyway?
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 disk delete call https://github.com/kubernetes-sigs/cluster-api-provider-azure/pull/2146/files#diff-5a6d880b9a0be00329894fc2f52f15827dd6bdfe6b60aaec660728661023f41fL142 but no disk reconcile call. Disk reconcile is a no-op so adding it does not change behavior.
/retest |
3cc357c
to
eb6756e
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.
I love this changeset! This improves the readability of the reconciler so much.
/lgtm
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
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
/assign @shysank
@@ -152,46 +102,14 @@ func (s *azureClusterService) Delete(ctx context.Context) error { | |||
ctx, _, done := tele.StartSpanWithLogger(ctx, "controllers.azureClusterService.Delete") | |||
defer done() | |||
|
|||
if err := s.groupsSvc.Delete(ctx); err != nil { | |||
if err := s.services[0].Delete(ctx); err != nil { |
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.
Could we set a variable like groupSvc
before placing it in the list so we call that explicitly 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.
Or can we make service registration a little bit more elaborate instead of just adding to an array. Something like
type RecocilerService struct {
Name string,
azure.Reconciler
}
func Register(serviceName, azure.Reconciler) {
}
This would help us easily add new apis to control reconciliation flow. For eg. for the case above, we can have a GetService(name)
which iterates over the services list and returns the one with matching name.
Or add a Skip
option to the ReconcilerService
if we want to skip reconciliation etc. wdyt?
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.
even if we assign it to a variable it wouldn't be accessible from here unless we add it as a separate field on the azureClusterService
struct since we only have access to the s
receiver from this func. we could assign a new variable here to make it explicit that it's what we expect but the variable would only be used once and it would still make the same assumption on the order so not sure that's an improvement, like this:
groupSvc := s.services[0]
if err := s.groupSvc.Delete(ctx); err != nil {
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.
@shysank I like the idea of registering the services. How would you set order and iterate over all the services without a list?
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.
Could we make a pretty simple struct with an array and a map from a name to its element in the array? Something like
type ServiceWrapper struct {
Services []azure.Reconciler
ServiceMap map[string]azure.Reconciler
}
func (s *ServiceWrapper) Add(name string, service azure.Reconciler) {
s.Services = append(s.Services, service)
s.ServiceMap[name] = service
}
func (s *ServiceWrapper) Get(name string) azure.Reconciler {
return s.ServiceMap[name]
}
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 would you set order and iterate over all the services without a list?
There will still be some collection backing the services, just that it'd be something more than just []azure.Reonciler. Something like []ReconcilerService
(or servicesMap in @Jont828's example). If I want to add a new service, I'll just use the Register
or Add
api without having to care about the underlying collection. For ordering, we could start with the order in which the services were registered (add priorities later if needed). We can extend this further by tagging services from which we can extract groups. For example, there can be a group with services independent of each other, and services in that group can be reconciled concurrently while the group itself will be reconciled serially. (shameless plug of a poc that I did earlier :) ).
Anyways, I'm fine with merging this PR with the current design since there has been so many reviews already, and we can discuss this in a separate issue.
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.
Let me try something out. Concurrency is out of scope and we should discuss it separately but I think I can at least get a service name added so we can log the service name when there's an error to @Jont828's point below.
inboundnatrules.New(machineScope), | ||
networkinterfaces.New(machineScope, cache), | ||
availabilitysets.New(machineScope, cache), | ||
disks.New(machineScope), |
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.
What was our behavior before this change? Would the disk get deleted anyway?
Okay @Jont828 @shysank check out the latest commits. It is a significantly larger refactor but I think it addresses your concerns. I added a new interface called I also included AzureMachinePool and AzureManagedControlPlane reconcilers in the changes since they use the same pattern. I think this gets us closer to what we want and we can discuss adding "groups" of services (or priorities, or a dependency graph) if we want to move towards concurrency as a follow-up. The |
type Reconciler interface { | ||
Reconcile(ctx context.Context) error | ||
Delete(ctx context.Context) error | ||
} | ||
|
||
// CredentialGetter is a Service which knows how to retrieve credentials for an Azure | ||
// resource in a resource group. | ||
type CredentialGetter 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.
this wasn't used anymore
@@ -219,3 +226,9 @@ func (s *Service) isVnetLinkManaged(ctx context.Context, resourceGroupName, zone | |||
tags := converters.MapToTags(zone.Tags) | |||
return tags.HasOwned(s.Scope.ClusterName()), nil | |||
} | |||
|
|||
// IsManaged returns always returns true. | |||
// TODO: separate private DNS and VNet links so we can implement the IsManaged method for each. |
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 one is a TODO for after #1715 is merged
@@ -149,3 +156,8 @@ func (s *Service) isIPManaged(ctx context.Context, ipName string) (bool, error) | |||
tags := converters.MapToTags(ip.Tags) | |||
return tags.HasOwned(s.Scope.ClusterName()), nil | |||
} | |||
|
|||
// IsManaged returns always returns true as public IPs are managed on a one-by-one basis. |
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 one is a little awkward. Each public IP can be either managed or unmanaged depending on its own tags and there is no overall "resource type is managed by CAPZ" or not, which makes it different from how the other services behave. Since we're not using this func anywhere (but it's there to satisfy the interface) I opted to always return true since that's the most cautious outcome: we assume public IPs are managed, but then we check on each one individually (https://github.com/kubernetes-sigs/cluster-api-provider-azure/pull/2146/files#diff-fe943e74189aa531d871d7b774c371755043f5bd1b7e8ab14ad3d2b5fc023301R148). Open to suggestions if anyone has a better idea.
cbbe562
to
b6b8bd4
Compare
looks like e2e tests are failing delete... looking |
b6b8bd4
to
2defff0
Compare
/lgtm |
should I squash or keep the commits separate? |
I'm fine with keeping the commits as is. |
I like the interface changes! |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: shysank 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:
Approvers can indicate their approval by writing |
@CecileRobertMichon: The following tests failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. 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. I understand the commands that are listed here. |
What type of PR is this?
/kind cleanup
What this PR does / why we need it: Since we are reconciling (and deleting in reverse order) Azure services one by one in reconcilers, this is a small optimization to move the reconcile and delete to a list and range through the services instead of making a Reconcile/Delete call for each. This is possible now that we've refactored all services to implement
Azure.Reconciler
. Also adds unit tests.Which issue(s) this PR fixes (optional, in
fixes #<issue number>(, fixes #<issue_number>, ...)
format, will close the issue(s) when PR gets merged):Fixes #
Special notes for your reviewer:
Please confirm that if this PR changes any image versions, then that's the sole change this PR makes.
TODOs:
Release note: