@@ -18,33 +18,35 @@ package virtualnetworks
1818
1919import (
2020 "context"
21+ "encoding/json"
2122
2223 "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network"
2324 "github.com/Azure/go-autorest/autorest"
25+ azureautorest "github.com/Azure/go-autorest/autorest/azure"
26+ "github.com/pkg/errors"
27+ infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1"
2428
2529 "sigs.k8s.io/cluster-api-provider-azure/azure"
30+ "sigs.k8s.io/cluster-api-provider-azure/util/reconciler"
2631 "sigs.k8s.io/cluster-api-provider-azure/util/tele"
2732)
2833
29- // Client wraps go-sdk.
30- type Client interface {
31- Get (context.Context , string , string ) (network.VirtualNetwork , error )
32- CreateOrUpdate (context.Context , string , string , network.VirtualNetwork ) error
33- Delete (context.Context , string , string ) error
34- CheckIPAddressAvailability (context.Context , string , string , string ) (network.IPAddressAvailabilityResult , error )
34+ // Getter is an interface that can get a virtual network.
35+ type Getter interface {
36+ Get (ctx context.Context , spec azure.ResourceSpecGetter ) (result interface {}, err error )
3537}
3638
37- // AzureClient contains the Azure go-sdk Client.
38- type AzureClient struct {
39+ // azureClient contains the Azure go-sdk Client.
40+ type azureClient struct {
3941 virtualnetworks network.VirtualNetworksClient
4042}
4143
42- var _ Client = & AzureClient {}
44+ var _ Getter = & azureClient {}
4345
44- // NewClient creates a new VM client from subscription ID.
45- func NewClient (auth azure.Authorizer ) * AzureClient {
46+ // newClient creates a new VM client from subscription ID.
47+ func newClient (auth azure.Authorizer ) * azureClient {
4648 c := newVirtualNetworksClient (auth .SubscriptionID (), auth .BaseURI (), auth .Authorizer ())
47- return & AzureClient {
49+ return & azureClient {
4850 virtualnetworks : c ,
4951 }
5052}
@@ -56,52 +58,113 @@ func newVirtualNetworksClient(subscriptionID string, baseURI string, authorizer
5658 return vnetsClient
5759}
5860
59- // Get gets the specified virtual network by resource group .
60- func (ac * AzureClient ) Get (ctx context.Context , resourceGroupName , vnetName string ) (network. VirtualNetwork , error ) {
61- ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.AzureClient .Get" )
61+ // Get gets the specified virtual network.
62+ func (ac * azureClient ) Get (ctx context.Context , spec azure. ResourceSpecGetter ) (result interface {}, err error ) {
63+ ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.azureClient .Get" )
6264 defer done ()
6365
64- return ac .virtualnetworks .Get (ctx , resourceGroupName , vnetName , "" )
66+ return ac .virtualnetworks .Get (ctx , spec . ResourceGroupName (), spec . ResourceName () , "" )
6567}
6668
67- // CreateOrUpdate creates or updates a virtual network in the specified resource group.
68- func (ac * AzureClient ) CreateOrUpdate (ctx context.Context , resourceGroupName , vnetName string , vn network.VirtualNetwork ) error {
69- ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.AzureClient.CreateOrUpdate" )
69+ // CreateOrUpdateAsync creates or updates a virtual network in the specified resource group asynchronously.
70+ // 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
71+ // progress of the operation.
72+ func (ac * azureClient ) CreateOrUpdateAsync (ctx context.Context , spec azure.ResourceSpecGetter , parameters interface {}) (result interface {}, future azureautorest.FutureAPI , err error ) {
73+ ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.azureClient.CreateOrUpdateAsync" )
7074 defer done ()
7175
72- future , err := ac .virtualnetworks .CreateOrUpdate (ctx , resourceGroupName , vnetName , vn )
76+ vn , ok := parameters .(network.VirtualNetwork )
77+ if ! ok {
78+ return nil , nil , errors .Errorf ("%T is not a network.VirtualNetwork" , parameters )
79+ }
80+
81+ createFuture , err := ac .virtualnetworks .CreateOrUpdate (ctx , spec .ResourceGroupName (), spec .ResourceName (), vn )
7382 if err != nil {
74- return err
83+ return nil , nil , err
7584 }
76- err = future .WaitForCompletionRef (ctx , ac .virtualnetworks .Client )
85+
86+ ctx , cancel := context .WithTimeout (ctx , reconciler .DefaultAzureCallTimeout )
87+ defer cancel ()
88+
89+ err = createFuture .WaitForCompletionRef (ctx , ac .virtualnetworks .Client )
7790 if err != nil {
78- return err
91+ // if an error occurs, return the future.
92+ // this means the long-running operation didn't finish in the specified timeout.
93+ return nil , & createFuture , err
7994 }
80- _ , err = future .Result (ac .virtualnetworks )
81- return err
95+ result , err = createFuture .Result (ac .virtualnetworks )
96+ // if the operation completed, return a nil future.
97+ return result , nil , err
8298}
8399
84- // Delete deletes the specified virtual network.
85- func (ac * AzureClient ) Delete (ctx context.Context , resourceGroupName , vnetName string ) error {
86- ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.AzureClient.Delete" )
100+ // DeleteAsync deletes a virtual network asynchronously. DeleteAsync sends a DELETE
101+ // request to Azure and if accepted without error, the func will return a Future which can be used to track the ongoing
102+ // progress of the operation.
103+ func (ac * azureClient ) DeleteAsync (ctx context.Context , spec azure.ResourceSpecGetter ) (future azureautorest.FutureAPI , err error ) {
104+ ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.azureClient.DeleteAsync" )
87105 defer done ()
88106
89- future , err := ac .virtualnetworks .Delete (ctx , resourceGroupName , vnetName )
107+ deleteFuture , err := ac .virtualnetworks .Delete (ctx , spec .ResourceGroupName (), spec .ResourceName ())
108+ if err != nil {
109+ return nil , err
110+ }
111+
112+ ctx , cancel := context .WithTimeout (ctx , reconciler .DefaultAzureCallTimeout )
113+ defer cancel ()
114+
115+ err = deleteFuture .WaitForCompletionRef (ctx , ac .virtualnetworks .Client )
90116 if err != nil {
91- return err
117+ // if an error occurs, return the future.
118+ // this means the long-running operation didn't finish in the specified timeout.
119+ return & deleteFuture , err
92120 }
93- err = future .WaitForCompletionRef (ctx , ac .virtualnetworks .Client )
121+ _ , err = deleteFuture .Result (ac .virtualnetworks )
122+ // if the operation completed, return a nil future.
123+ return nil , err
124+ }
125+
126+ // IsDone returns true if the long-running operation has completed.
127+ func (ac * azureClient ) IsDone (ctx context.Context , future azureautorest.FutureAPI ) (isDone bool , err error ) {
128+ ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.azureClient.IsDone" )
129+ defer done ()
130+
131+ isDone , err = future .DoneWithContext (ctx , ac .virtualnetworks )
94132 if err != nil {
95- return err
133+ return false , errors . Wrap ( err , "failed checking if the operation was complete" )
96134 }
97- _ , err = future . Result ( ac . virtualnetworks )
98- return err
135+
136+ return isDone , nil
99137}
100138
101- // CheckIPAddressAvailability checks whether a private IP address is available for use .
102- func (ac * AzureClient ) CheckIPAddressAvailability (ctx context.Context , resourceGroupName , vnetName , ip string ) (network. IPAddressAvailabilityResult , error ) {
103- ctx , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.AzureClient.CheckIPAddressAvailability " )
139+ // Result fetches the result of a long-running operation future .
140+ func (ac * azureClient ) Result (ctx context.Context , future azureautorest. FutureAPI , futureType string ) (result interface {}, err error ) {
141+ _ , _ , done := tele .StartSpanWithLogger (ctx , "virtualnetworks.azureClient.Result " )
104142 defer done ()
105143
106- return ac .virtualnetworks .CheckIPAddressAvailability (ctx , resourceGroupName , vnetName , ip )
144+ if future == nil {
145+ return nil , errors .Errorf ("cannot get result from nil future" )
146+ }
147+
148+ switch futureType {
149+ case infrav1 .PutFuture :
150+ // Marshal and Unmarshal the future to put it into the correct future type so we can access the Result function.
151+ // Unfortunately the FutureAPI can't be casted directly to VirtualNetworksCreateOrUpdateFuture because it is a azureautorest.Future, which doesn't implement the Result function. See PR #1686 for discussion on alternatives.
152+ // 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.
153+ var createFuture * network.VirtualNetworksCreateOrUpdateFuture
154+ jsonData , err := future .MarshalJSON ()
155+ if err != nil {
156+ return nil , errors .Wrap (err , "failed to marshal future" )
157+ }
158+ if err := json .Unmarshal (jsonData , & createFuture ); err != nil {
159+ return nil , errors .Wrap (err , "failed to unmarshal future data" )
160+ }
161+ return (* createFuture ).Result (ac .virtualnetworks )
162+
163+ case infrav1 .DeleteFuture :
164+ // Delete does not return a result vnet.
165+ return nil , nil
166+
167+ default :
168+ return nil , errors .Errorf ("unknown future type %q" , futureType )
169+ }
107170}
0 commit comments