diff --git a/azure/services/privatedns/client.go b/azure/services/privatedns/client.go deleted file mode 100644 index f4f9e2b8a103..000000000000 --- a/azure/services/privatedns/client.go +++ /dev/null @@ -1,184 +0,0 @@ -/* -Copyright 2019 The Kubernetes Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package privatedns - -import ( - "context" - - "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" - "github.com/Azure/go-autorest/autorest" - - "sigs.k8s.io/cluster-api-provider-azure/azure" - "sigs.k8s.io/cluster-api-provider-azure/util/tele" -) - -// Client wraps go-sdk. -type client interface { - GetZone(context.Context, string, string) (privatedns.PrivateZone, error) - CreateOrUpdateZone(context.Context, string, string, privatedns.PrivateZone) error - DeleteZone(context.Context, string, string) error - GetLink(context.Context, string, string, string) (privatedns.VirtualNetworkLink, error) - CreateOrUpdateLink(context.Context, string, string, string, privatedns.VirtualNetworkLink) error - DeleteLink(context.Context, string, string, string) error - CreateOrUpdateRecordSet(context.Context, string, string, privatedns.RecordType, string, privatedns.RecordSet) error - DeleteRecordSet(context.Context, string, string, privatedns.RecordType, string) error -} - -// AzureClient contains the Azure go-sdk Client. -type azureClient struct { - privatezones privatedns.PrivateZonesClient - vnetlinks privatedns.VirtualNetworkLinksClient - recordsets privatedns.RecordSetsClient -} - -var _ client = (*azureClient)(nil) - -// newClient creates a new VM client from subscription ID. -func newClient(auth azure.Authorizer) *azureClient { - c := newPrivateZonesClient(auth.SubscriptionID(), auth.BaseURI(), auth.Authorizer()) - v := newVirtualNetworkLinksClient(auth.SubscriptionID(), auth.BaseURI(), auth.Authorizer()) - r := newRecordSetsClient(auth.SubscriptionID(), auth.BaseURI(), auth.Authorizer()) - return &azureClient{c, v, r} -} - -// newPrivateZonesClient creates a new private zones client from subscription ID. -func newPrivateZonesClient(subscriptionID string, baseURI string, authorizer autorest.Authorizer) privatedns.PrivateZonesClient { - zonesClient := privatedns.NewPrivateZonesClientWithBaseURI(baseURI, subscriptionID) - azure.SetAutoRestClientDefaults(&zonesClient.Client, authorizer) - return zonesClient -} - -// newVirtualNetworkLinksClient creates a new virtual networks link client from subscription ID. -func newVirtualNetworkLinksClient(subscriptionID string, baseURI string, authorizer autorest.Authorizer) privatedns.VirtualNetworkLinksClient { - linksClient := privatedns.NewVirtualNetworkLinksClientWithBaseURI(baseURI, subscriptionID) - azure.SetAutoRestClientDefaults(&linksClient.Client, authorizer) - return linksClient -} - -// newRecordSetsClient creates a new record sets client from subscription ID. -func newRecordSetsClient(subscriptionID string, baseURI string, authorizer autorest.Authorizer) privatedns.RecordSetsClient { - recordsClient := privatedns.NewRecordSetsClientWithBaseURI(baseURI, subscriptionID) - azure.SetAutoRestClientDefaults(&recordsClient.Client, authorizer) - return recordsClient -} - -// GetZone returns a private zone. -func (ac *azureClient) GetZone(ctx context.Context, resourceGroupName, zoneName string) (privatedns.PrivateZone, error) { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.GetZone") - defer done() - zone, err := ac.privatezones.Get(ctx, resourceGroupName, zoneName) - if err != nil { - return privatedns.PrivateZone{}, err - } - return zone, nil -} - -// CreateOrUpdateZone creates or updates a private zone. -func (ac *azureClient) CreateOrUpdateZone(ctx context.Context, resourceGroupName string, zoneName string, zone privatedns.PrivateZone) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.CreateOrUpdateZone") - defer done() - future, err := ac.privatezones.CreateOrUpdate(ctx, resourceGroupName, zoneName, zone, "", "") - if err != nil { - return err - } - err = future.WaitForCompletionRef(ctx, ac.privatezones.Client) - if err != nil { - return err - } - _, err = future.Result(ac.privatezones) - return err -} - -// DeleteZone deletes the private zone. -func (ac *azureClient) DeleteZone(ctx context.Context, resourceGroupName, name string) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.DeleteZone") - defer done() - - future, err := ac.privatezones.Delete(ctx, resourceGroupName, name, "") - if err != nil { - return err - } - err = future.WaitForCompletionRef(ctx, ac.privatezones.Client) - if err != nil { - return err - } - _, err = future.Result(ac.privatezones) - return err -} - -// GetLink returns a vnet link. -func (ac *azureClient) GetLink(ctx context.Context, resourceGroupName, zoneName, vnetLinkName string) (privatedns.VirtualNetworkLink, error) { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.GetLink") - defer done() - vnetLink, err := ac.vnetlinks.Get(ctx, resourceGroupName, zoneName, vnetLinkName) - if err != nil { - return privatedns.VirtualNetworkLink{}, err - } - return vnetLink, nil -} - -// CreateOrUpdateLink creates or updates a virtual network link to the specified Private DNS zone. -func (ac *azureClient) CreateOrUpdateLink(ctx context.Context, resourceGroupName, privateZoneName, name string, link privatedns.VirtualNetworkLink) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.CreateOrUpdateLink") - defer done() - - future, err := ac.vnetlinks.CreateOrUpdate(ctx, resourceGroupName, privateZoneName, name, link, "", "") - if err != nil { - return err - } - err = future.WaitForCompletionRef(ctx, ac.vnetlinks.Client) - if err != nil { - return err - } - _, err = future.Result(ac.vnetlinks) - return err -} - -// DeleteLink deletes a virtual network link to the specified Private DNS zone. -func (ac *azureClient) DeleteLink(ctx context.Context, resourceGroupName, privateZoneName, name string) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.DeleteLink") - defer done() - - future, err := ac.vnetlinks.Delete(ctx, resourceGroupName, privateZoneName, name, "") - if err != nil { - return err - } - err = future.WaitForCompletionRef(ctx, ac.vnetlinks.Client) - if err != nil { - return err - } - _, err = future.Result(ac.vnetlinks) - return err -} - -// CreateOrUpdateRecordSet creates or updates a record set within the specified Private DNS zone. -func (ac *azureClient) CreateOrUpdateRecordSet(ctx context.Context, resourceGroupName string, privateZoneName string, recordType privatedns.RecordType, name string, set privatedns.RecordSet) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.CreateOrUpdateRecordSet") - defer done() - - _, err := ac.recordsets.CreateOrUpdate(ctx, resourceGroupName, privateZoneName, recordType, name, set, "", "") - return err -} - -// DeleteRecordSet deletes a record set within the specified Private DNS zone. -func (ac *azureClient) DeleteRecordSet(ctx context.Context, resourceGroupName string, privateZoneName string, recordType privatedns.RecordType, name string) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.AzureClient.DeleteRecordSet") - defer done() - - _, err := ac.recordsets.Delete(ctx, resourceGroupName, privateZoneName, recordType, name, "") - return err -} diff --git a/azure/services/privatedns/client_records.go b/azure/services/privatedns/client_records.go new file mode 100644 index 000000000000..4dec69278539 --- /dev/null +++ b/azure/services/privatedns/client_records.go @@ -0,0 +1,94 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package privatedns + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + azureautorest "github.com/Azure/go-autorest/autorest/azure" + "github.com/pkg/errors" + "sigs.k8s.io/cluster-api-provider-azure/azure" + "sigs.k8s.io/cluster-api-provider-azure/util/tele" +) + +// azureRecordsClient contains the Azure go-sdk Client for record sets. +type azureRecordsClient struct { + recordsets privatedns.RecordSetsClient +} + +// newRecordSetsClient creates a new record sets client from subscription ID. +func newRecordSetsClient(auth azure.Authorizer) *azureRecordsClient { + recordsClient := privatedns.NewRecordSetsClientWithBaseURI(auth.BaseURI(), auth.SubscriptionID()) + azure.SetAutoRestClientDefaults(&recordsClient.Client, auth.Authorizer()) + return &azureRecordsClient{ + recordsets: recordsClient, + } +} + +// CreateOrUpdateAsync creates or updates a record asynchronously. +// Well, I lied, CreateOrUpdateAsync creates a record synchronously. This is because azure api for records does not futures yet. +// TODO: make it async once azure api for records gets future support. +func (arc *azureRecordsClient) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, parameters interface{}) (result interface{}, future azureautorest.FutureAPI, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureRecordsClient.CreateOrUpdateAsync") + defer done() + + set, ok := parameters.(privatedns.RecordSet) + if !ok { + return nil, nil, errors.Errorf("%T is not a privatedns.RecordSet", parameters) + } + + // Determine record type. + var ( + recordType privatedns.RecordType + aRecords = set.RecordSetProperties.ARecords + aaaRecords = set.RecordSetProperties.AaaaRecords + ) + if aRecords != nil && len(*aRecords) > 0 && (*aRecords)[0].Ipv4Address != nil { + recordType = privatedns.A + } else if aaaRecords != nil && len(*aaaRecords) > 0 && (*aaaRecords)[0].Ipv6Address != nil { + recordType = privatedns.AAAA + } + + recordSet, err := arc.recordsets.CreateOrUpdate(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), recordType, spec.ResourceName(), set, "", "") + if err != nil { + return nil, nil, err + } + + return recordSet, nil, err +} + +// Get gets the specified record set. Noop for records. +func (arc *azureRecordsClient) Get(ctx context.Context, spec azure.ResourceSpecGetter) (result interface{}, err error) { + return nil, nil +} + +// DeleteAsync deletes a record asynchronously. Noop for records. +// TODO: implement Delete for records. +func (arc *azureRecordsClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter) (future azureautorest.FutureAPI, err error) { + return nil, nil +} + +// IsDone returns true if the long-running operation has completed. Noop for records. +func (arc *azureRecordsClient) IsDone(ctx context.Context, future azureautorest.FutureAPI) (isDone bool, err error) { + return true, nil +} + +// Result fetches the result of a long-running operation future. Noop for records. +func (arc *azureRecordsClient) Result(ctx context.Context, future azureautorest.FutureAPI, futureType string) (result interface{}, err error) { + return nil, nil +} diff --git a/azure/services/privatedns/client_vnetlink.go b/azure/services/privatedns/client_vnetlink.go new file mode 100644 index 000000000000..19b614b586dd --- /dev/null +++ b/azure/services/privatedns/client_vnetlink.go @@ -0,0 +1,158 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package privatedns + +import ( + "context" + "encoding/json" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + azureautorest "github.com/Azure/go-autorest/autorest/azure" + "github.com/pkg/errors" + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" + "sigs.k8s.io/cluster-api-provider-azure/azure" + "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" + "sigs.k8s.io/cluster-api-provider-azure/util/tele" +) + +// azureVirtualNetworkLinksClient contains the Azure go-sdk Client for virtual network links. +type azureVirtualNetworkLinksClient struct { + vnetlinks privatedns.VirtualNetworkLinksClient +} + +// newVirtualNetworkLinksClient creates a new virtual networks link client from subscription ID. +func newVirtualNetworkLinksClient(auth azure.Authorizer) *azureVirtualNetworkLinksClient { + linksClient := privatedns.NewVirtualNetworkLinksClientWithBaseURI(auth.BaseURI(), auth.SubscriptionID()) + azure.SetAutoRestClientDefaults(&linksClient.Client, auth.Authorizer()) + return &azureVirtualNetworkLinksClient{ + vnetlinks: linksClient, + } +} + +// CreateOrUpdateAsync creates or updates a virtual network link asynchronously. +// It sends a PUT request to Azure and if accepted without error, the func will return a Future which can be used to track the ongoing +// progress of the operation. +func (avc *azureVirtualNetworkLinksClient) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, parameters interface{}) (result interface{}, future azureautorest.FutureAPI, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureVirtualNetworkLinksClient.CreateOrUpdateAsync") + defer done() + + link, ok := parameters.(privatedns.VirtualNetworkLink) + if !ok { + return nil, nil, errors.Errorf("%T is not a privatedns.VirtualNetworkLink", parameters) + } + + createFuture, err := avc.vnetlinks.CreateOrUpdate(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceGroupName(), link, "", "") + if err != nil { + return nil, nil, err + } + + ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout) + defer cancel() + + err = createFuture.WaitForCompletionRef(ctx, avc.vnetlinks.Client) + if err != nil { + // if an error occurs, return the future. + // this means the long-running operation didn't finish in the specified timeout. + return nil, &createFuture, err + } + result, err = createFuture.Result(avc.vnetlinks) + // if the operation completed, return a nil future + return result, nil, err +} + +// Get gets the specified virtual network link. +func (avc *azureVirtualNetworkLinksClient) Get(ctx context.Context, spec azure.ResourceSpecGetter) (result interface{}, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureVirtualNetworkLinksClient.Get") + defer done() + zone, err := avc.vnetlinks.Get(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName()) + if err != nil { + return privatedns.PrivateZone{}, err + } + return zone, nil +} + +// DeleteAsync deletes a virtual network link asynchronously. DeleteAsync sends a DELETE +// request to Azure and if accepted without error, the func will return a Future which can be used to track the ongoing +// progress of the operation. +func (avc *azureVirtualNetworkLinksClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter) (future azureautorest.FutureAPI, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureVirtualNetworkLinksClient.DeleteAsync") + defer done() + + deleteFuture, err := avc.vnetlinks.Delete(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName(), "") + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout) + defer cancel() + + err = deleteFuture.WaitForCompletionRef(ctx, avc.vnetlinks.Client) + if err != nil { + // if an error occurs, return the future. + // this means the long-running operation didn't finish in the specified timeout. + return &deleteFuture, err + } + _, err = deleteFuture.Result(avc.vnetlinks) + // if the operation completed, return a nil future. + return nil, err +} + +// IsDone returns true if the long-running operation has completed. +func (avc *azureVirtualNetworkLinksClient) IsDone(ctx context.Context, future azureautorest.FutureAPI) (isDone bool, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureVirtualNetworkLinksClient.IsDone") + defer done() + + isDone, err = future.DoneWithContext(ctx, avc.vnetlinks) + if err != nil { + return false, errors.Wrap(err, "failed checking if the operation was complete") + } + + return isDone, nil +} + +// Result fetches the result of a long-running operation future. +func (avc *azureVirtualNetworkLinksClient) Result(ctx context.Context, future azureautorest.FutureAPI, futureType string) (result interface{}, err error) { + _, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureVirtualNetworkLinksClient.Result") + defer done() + + if future == nil { + return nil, errors.Errorf("cannot get result from nil future") + } + + switch futureType { + case infrav1.PutFuture: + // Marshal and Unmarshal the future to put it into the correct future type so we can access the Result function. + // Unfortunately the FutureAPI can't be casted directly to VirtualNetworkLinksCreateOrUpdateFuture because it is a azureautorest.Future, which doesn't implement the Result function. See PR #1686 for discussion on alternatives. + // It was converted back to a generic azureautorest.Future from the CAPZ infrav1.Future type stored in Status: https://github.com/kubernetes-sigs/cluster-api-provider-azure/blob/main/azure/converters/futures.go#L49. + var createFuture *privatedns.VirtualNetworkLinksCreateOrUpdateFuture + jsonData, err := future.MarshalJSON() + if err != nil { + return nil, errors.Wrap(err, "failed to marshal future") + } + if err := json.Unmarshal(jsonData, &createFuture); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal future data") + } + return (*createFuture).Result(avc.vnetlinks) + + case infrav1.DeleteFuture: + // Delete does not return a result route table. + return nil, nil + + default: + return nil, errors.Errorf("unknown future type %q", futureType) + } +} diff --git a/azure/services/privatedns/client_zone.go b/azure/services/privatedns/client_zone.go new file mode 100644 index 000000000000..95a2f22a4262 --- /dev/null +++ b/azure/services/privatedns/client_zone.go @@ -0,0 +1,158 @@ +/* +Copyright 2022 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package privatedns + +import ( + "context" + "encoding/json" + + "github.com/Azure/azure-sdk-for-go/services/privatedns/mgmt/2018-09-01/privatedns" + azureautorest "github.com/Azure/go-autorest/autorest/azure" + "github.com/pkg/errors" + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" + "sigs.k8s.io/cluster-api-provider-azure/azure" + "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" + "sigs.k8s.io/cluster-api-provider-azure/util/tele" +) + +// azureZonesClient contains the Azure go-sdk Client for private dns zone. +type azureZonesClient struct { + privatezones privatedns.PrivateZonesClient +} + +// newPrivateZonesClient creates a new private zones client from subscription ID. +func newPrivateZonesClient(auth azure.Authorizer) *azureZonesClient { + c := privatedns.NewPrivateZonesClientWithBaseURI(auth.BaseURI(), auth.SubscriptionID()) + azure.SetAutoRestClientDefaults(&c.Client, auth.Authorizer()) + return &azureZonesClient{ + privatezones: c, + } +} + +// CreateOrUpdateAsync creates or updates a private dns zone asynchronously. +// It sends a PUT request to Azure and if accepted without error, the func will return a Future which can be used to track the ongoing +// progress of the operation. +func (azc *azureZonesClient) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, parameters interface{}) (result interface{}, future azureautorest.FutureAPI, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureZonesClient.CreateOrUpdateAsync") + defer done() + + zone, ok := parameters.(privatedns.PrivateZone) + if !ok { + return nil, nil, errors.Errorf("%T is not a privatedns.PrivateZone", parameters) + } + + createFuture, err := azc.privatezones.CreateOrUpdate(ctx, spec.ResourceGroupName(), spec.ResourceName(), zone, "", "") + if err != nil { + return nil, nil, err + } + + ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout) + defer cancel() + + err = createFuture.WaitForCompletionRef(ctx, azc.privatezones.Client) + if err != nil { + // if an error occurs, return the future. + // this means the long-running operation didn't finish in the specified timeout. + return nil, &createFuture, err + } + result, err = createFuture.Result(azc.privatezones) + // if the operation completed, return a nil future + return result, nil, err +} + +// Get gets the specified private dns zone. +func (azc *azureZonesClient) Get(ctx context.Context, spec azure.ResourceSpecGetter) (result interface{}, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureZonesClient.Get") + defer done() + zone, err := azc.privatezones.Get(ctx, spec.ResourceGroupName(), spec.ResourceName()) + if err != nil { + return privatedns.PrivateZone{}, err + } + return zone, nil +} + +// DeleteAsync deletes a private dns zone asynchronously. DeleteAsync sends a DELETE +// request to Azure and if accepted without error, the func will return a Future which can be used to track the ongoing +// progress of the operation. +func (azc *azureZonesClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter) (future azureautorest.FutureAPI, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureZonesClient.DeleteAsync") + defer done() + + deleteFuture, err := azc.privatezones.Delete(ctx, spec.ResourceGroupName(), spec.ResourceName(), "") + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout) + defer cancel() + + err = deleteFuture.WaitForCompletionRef(ctx, azc.privatezones.Client) + if err != nil { + // if an error occurs, return the future. + // this means the long-running operation didn't finish in the specified timeout. + return &deleteFuture, err + } + _, err = deleteFuture.Result(azc.privatezones) + // if the operation completed, return a nil future. + return nil, err +} + +// IsDone returns true if the long-running operation has completed. +func (azc *azureZonesClient) IsDone(ctx context.Context, future azureautorest.FutureAPI) (isDone bool, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureZonesClient.IsDone") + defer done() + + isDone, err = future.DoneWithContext(ctx, azc.privatezones) + if err != nil { + return false, errors.Wrap(err, "failed checking if the operation was complete") + } + + return isDone, nil +} + +// Result fetches the result of a long-running operation future. +func (azc *azureZonesClient) Result(ctx context.Context, future azureautorest.FutureAPI, futureType string) (result interface{}, err error) { + _, _, done := tele.StartSpanWithLogger(ctx, "privatedns.azureZonesClient.Result") + defer done() + + if future == nil { + return nil, errors.Errorf("cannot get result from nil future") + } + + switch futureType { + case infrav1.PutFuture: + // Marshal and Unmarshal the future to put it into the correct future type so we can access the Result function. + // Unfortunately the FutureAPI can't be casted directly to PrivateZonesCreateOrUpdateFuture because it is a azureautorest.Future, which doesn't implement the Result function. See PR #1686 for discussion on alternatives. + // It was converted back to a generic azureautorest.Future from the CAPZ infrav1.Future type stored in Status: https://github.com/kubernetes-sigs/cluster-api-provider-azure/blob/main/azure/converters/futures.go#L49. + var createFuture *privatedns.PrivateZonesCreateOrUpdateFuture + jsonData, err := future.MarshalJSON() + if err != nil { + return nil, errors.Wrap(err, "failed to marshal future") + } + if err := json.Unmarshal(jsonData, &createFuture); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal future data") + } + return (*createFuture).Result(azc.privatezones) + + case infrav1.DeleteFuture: + // Delete does not return a result route table. + return nil, nil + + default: + return nil, errors.Errorf("unknown future type %q", futureType) + } +}