diff --git a/azure/services/inboundnatrules/client.go b/azure/services/inboundnatrules/client.go index bdb5d529dab..68a72325f5a 100644 --- a/azure/services/inboundnatrules/client.go +++ b/azure/services/inboundnatrules/client.go @@ -18,49 +18,39 @@ package inboundnatrules import ( "context" - "encoding/json" - "fmt" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" - "github.com/Azure/go-autorest/autorest" - azureautorest "github.com/Azure/go-autorest/autorest/azure" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" "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/azure/services/asyncpoller" "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" "sigs.k8s.io/cluster-api-provider-azure/util/tele" ) // client wraps go-sdk. type client interface { - List(context.Context, string, string) (result []network.InboundNatRule, err error) - Get(context.Context, azure.ResourceSpecGetter) (result interface{}, err error) - CreateOrUpdateAsync(context.Context, azure.ResourceSpecGetter, interface{}) (result interface{}, future azureautorest.FutureAPI, err error) - DeleteAsync(context.Context, azure.ResourceSpecGetter) (future azureautorest.FutureAPI, err error) - IsDone(context.Context, azureautorest.FutureAPI) (isDone bool, err error) - Result(context.Context, azureautorest.FutureAPI, string) (result interface{}, err error) + List(context.Context, string, string) (result []armnetwork.InboundNatRule, err error) } // azureClient contains the Azure go-sdk Client. type azureClient struct { - inboundnatrules network.InboundNatRulesClient + inboundnatrules *armnetwork.InboundNatRulesClient } var _ client = (*azureClient)(nil) -// newClient creates a new inbound NAT rules client from subscription ID. -func newClient(auth azure.Authorizer) *azureClient { - inboundNatRulesClient := newInboundNatRulesClient(auth.SubscriptionID(), auth.BaseURI(), auth.Authorizer()) - return &azureClient{ - inboundnatrules: inboundNatRulesClient, +// newClient creates a new inbound NAT rules client from an authorizer. +func newClient(auth azure.Authorizer) (*azureClient, error) { + opts, err := azure.ARMClientOptions(auth.CloudEnvironment()) + if err != nil { + return nil, errors.Wrap(err, "failed to create inboundnatrules client options") } -} - -// newInboundNatClient creates a new inbound NAT rules client from subscription ID. -func newInboundNatRulesClient(subscriptionID string, baseURI string, authorizer autorest.Authorizer) network.InboundNatRulesClient { - inboundNatRulesClient := network.NewInboundNatRulesClientWithBaseURI(baseURI, subscriptionID) - azure.SetAutoRestClientDefaults(&inboundNatRulesClient.Client, authorizer) - return inboundNatRulesClient + factory, err := armnetwork.NewClientFactory(auth.SubscriptionID(), auth.Token(), opts) + if err != nil { + return nil, errors.Wrap(err, "failed to create armnetwork client factory") + } + return &azureClient{factory.NewInboundNatRulesClient()}, nil } // Get gets the specified inbound NAT rules. @@ -68,43 +58,48 @@ func (ac *azureClient) Get(ctx context.Context, spec azure.ResourceSpecGetter) ( ctx, _, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.Get") defer done() - return ac.inboundnatrules.Get(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName(), "") + resp, err := ac.inboundnatrules.Get(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName(), nil) + if err != nil { + return nil, err + } + return resp.InboundNatRule, nil } // List returns all inbound NAT rules on a load balancer. -func (ac *azureClient) List(ctx context.Context, resourceGroupName, lbName string) (result []network.InboundNatRule, err error) { +func (ac *azureClient) List(ctx context.Context, resourceGroupName, lbName string) (result []armnetwork.InboundNatRule, err error) { ctx, _, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.List") defer done() - iter, err := ac.inboundnatrules.ListComplete(ctx, resourceGroupName, lbName) - if err != nil { - return nil, errors.Wrap(err, fmt.Sprintf("could not list inbound NAT rules for load balancer %s", lbName)) - } - - var natRules []network.InboundNatRule - for iter.NotDone() { - natRules = append(natRules, iter.Value()) - if err := iter.NextWithContext(ctx); err != nil { + var natRules []armnetwork.InboundNatRule + pager := ac.inboundnatrules.NewListPager(resourceGroupName, lbName, nil) + for pager.More() { + nextResult, err := pager.NextPage(ctx) + if err != nil { return natRules, errors.Wrap(err, "could not iterate inbound NAT rules") } + for _, natRule := range nextResult.Value { + natRules = append(natRules, *natRule) + } } return natRules, nil } // CreateOrUpdateAsync creates or updates an inbound NAT rule 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 +// It sends a PUT request to Azure and if accepted without error, the func will return a Poller which can be used to track the ongoing // progress of the operation. -func (ac *azureClient) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, parameters interface{}) (result interface{}, future azureautorest.FutureAPI, err error) { - ctx, _, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.CreateOrUpdateAsync") +func (ac *azureClient) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, resumeToken string, parameters interface{}) (result interface{}, poller *runtime.Poller[armnetwork.InboundNatRulesClientCreateOrUpdateResponse], err error) { + ctx, log, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.CreateOrUpdateAsync") defer done() - natRule, ok := parameters.(network.InboundNatRule) + natRule, ok := parameters.(armnetwork.InboundNatRule) if !ok { - return nil, nil, errors.Errorf("%T is not a network.InboundNatRule", parameters) + return nil, nil, errors.Errorf("%T is not an armnetwork.InboundNatRule", parameters) } - createFuture, err := ac.inboundnatrules.CreateOrUpdate(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName(), natRule) + opts := &armnetwork.InboundNatRulesClientBeginCreateOrUpdateOptions{ResumeToken: resumeToken} + log.V(4).Info("sending request", "resumeToken", resumeToken) + poller, err = ac.inboundnatrules.BeginCreateOrUpdate(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName(), natRule, opts) if err != nil { return nil, nil, err } @@ -112,26 +107,28 @@ func (ac *azureClient) CreateOrUpdateAsync(ctx context.Context, spec azure.Resou ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout) defer cancel() - err = createFuture.WaitForCompletionRef(ctx, ac.inboundnatrules.Client) + pollOpts := &runtime.PollUntilDoneOptions{Frequency: asyncpoller.DefaultPollerFrequency} + resp, err := poller.PollUntilDone(ctx, pollOpts) 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 + // If an error occurs, return the poller. + // This means the long-running operation didn't finish in the specified timeout. + return nil, poller, err } - result, err = createFuture.Result(ac.inboundnatrules) - // if the operation completed, return a nil future - return result, nil, err + // if the operation completed, return a nil poller + return resp.InboundNatRule, nil, err } // DeleteAsync deletes an inbound NAT rule 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 +// request to Azure and if accepted without error, the func will return a Poller which can be used to track the ongoing // progress of the operation. -func (ac *azureClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter) (future azureautorest.FutureAPI, err error) { - ctx, _, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.DeleteAsync") +func (ac *azureClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter, resumeToken string) (poller *runtime.Poller[armnetwork.InboundNatRulesClientDeleteResponse], err error) { + ctx, log, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.DeleteAsync") defer done() - deleteFuture, err := ac.inboundnatrules.Delete(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName()) + opts := &armnetwork.InboundNatRulesClientBeginDeleteOptions{ResumeToken: resumeToken} + log.V(4).Info("sending request", "resumeToken", resumeToken) + poller, err = ac.inboundnatrules.BeginDelete(ctx, spec.ResourceGroupName(), spec.OwnerResourceName(), spec.ResourceName(), opts) if err != nil { return nil, err } @@ -139,54 +136,13 @@ func (ac *azureClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecG ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout) defer cancel() - err = deleteFuture.WaitForCompletionRef(ctx, ac.inboundnatrules.Client) + pollOpts := &runtime.PollUntilDoneOptions{Frequency: asyncpoller.DefaultPollerFrequency} + _, err = poller.PollUntilDone(ctx, pollOpts) if err != nil { - // if an error occurs, return the future. + // if an error occurs, return the poller. // this means the long-running operation didn't finish in the specified timeout. - return &deleteFuture, err + return poller, err } - _, err = deleteFuture.Result(ac.inboundnatrules) - // if the operation completed, return a nil future. + // if the operation completed, return a nil poller. return nil, err } - -// IsDone returns true if the long-running operation has completed. -func (ac *azureClient) IsDone(ctx context.Context, future azureautorest.FutureAPI) (isDone bool, err error) { - ctx, _, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.IsDone") - defer done() - - return future.DoneWithContext(ctx, ac.inboundnatrules) -} - -// Result fetches the result of a long-running operation future. -func (ac *azureClient) Result(ctx context.Context, future azureautorest.FutureAPI, futureType string) (result interface{}, err error) { - _, _, done := tele.StartSpanWithLogger(ctx, "inboundnatrules.azureClient.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 InboundNatRulesCreateOrUpdateFuture 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 *network.InboundNatRulesCreateOrUpdateFuture - 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(ac.inboundnatrules) - - case infrav1.DeleteFuture: - // Delete does not return a result inbound NAT rule - return nil, nil - - default: - return nil, errors.Errorf("unknown future type %q", futureType) - } -} diff --git a/azure/services/inboundnatrules/inboundnatrules.go b/azure/services/inboundnatrules/inboundnatrules.go index e9f6260cd1e..b44b847304d 100644 --- a/azure/services/inboundnatrules/inboundnatrules.go +++ b/azure/services/inboundnatrules/inboundnatrules.go @@ -19,10 +19,11 @@ package inboundnatrules import ( "context" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" "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/azure/services/async" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/asyncpoller" "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" "sigs.k8s.io/cluster-api-provider-azure/util/tele" ) @@ -41,17 +42,21 @@ type InboundNatScope interface { type Service struct { Scope InboundNatScope client - async.Reconciler + asyncpoller.Reconciler } // New creates a new service. -func New(scope InboundNatScope) *Service { - client := newClient(scope) - return &Service{ - Scope: scope, - client: client, - Reconciler: async.New(scope, client, client), +func New(scope InboundNatScope) (*Service, error) { + client, err := newClient(scope) + if err != nil { + return nil, err } + return &Service{ + Scope: scope, + client: client, + Reconciler: asyncpoller.New[armnetwork.InboundNatRulesClientCreateOrUpdateResponse, + armnetwork.InboundNatRulesClientDeleteResponse](scope, client, client), + }, nil } // Name returns the service name. @@ -87,7 +92,7 @@ func (s *Service) Reconcile(ctx context.Context) error { portsInUse := make(map[int32]struct{}) for _, rule := range existingRules { - portsInUse[*rule.InboundNatRulePropertiesFormat.FrontendPort] = struct{}{} // Mark frontend port as in use + portsInUse[*rule.Properties.FrontendPort] = struct{}{} // Mark frontend port as in use } // We go through the list of InboundNatSpecs to reconcile each one, independently of the result of the previous one. diff --git a/azure/services/inboundnatrules/inboundnatrules_test.go b/azure/services/inboundnatrules/inboundnatrules_test.go index 2d1ce2f6e25..44ca569aa63 100644 --- a/azure/services/inboundnatrules/inboundnatrules_test.go +++ b/azure/services/inboundnatrules/inboundnatrules_test.go @@ -21,7 +21,7 @@ import ( "net/http" "testing" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" "github.com/Azure/go-autorest/autorest" . "github.com/onsi/gomega" "go.uber.org/mock/gomock" @@ -37,19 +37,19 @@ var ( fakeLBName = "my-lb-1" fakeGroupName = "my-rg" - noExistingRules = []network.InboundNatRule{} - fakeExistingRules = []network.InboundNatRule{ + noExistingRules = []armnetwork.InboundNatRule{} + fakeExistingRules = []armnetwork.InboundNatRule{ { Name: ptr.To("other-machine-nat-rule"), ID: ptr.To("some-natrules-id"), - InboundNatRulePropertiesFormat: &network.InboundNatRulePropertiesFormat{ + Properties: &armnetwork.InboundNatRulePropertiesFormat{ FrontendPort: ptr.To[int32](22), }, }, { Name: ptr.To("other-machine-nat-rule-2"), ID: ptr.To("some-natrules-id-2"), - InboundNatRulePropertiesFormat: &network.InboundNatRulePropertiesFormat{ + Properties: &armnetwork.InboundNatRulePropertiesFormat{ FrontendPort: ptr.To[int32](2201), }, }, diff --git a/azure/services/inboundnatrules/mock_inboundnatrules/client_mock.go b/azure/services/inboundnatrules/mock_inboundnatrules/client_mock.go index cb9b91250bb..479519fe728 100644 --- a/azure/services/inboundnatrules/mock_inboundnatrules/client_mock.go +++ b/azure/services/inboundnatrules/mock_inboundnatrules/client_mock.go @@ -24,10 +24,8 @@ import ( context "context" reflect "reflect" - network "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" - azure "github.com/Azure/go-autorest/autorest/azure" + armnetwork "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" gomock "go.uber.org/mock/gomock" - azure0 "sigs.k8s.io/cluster-api-provider-azure/azure" ) // Mockclient is a mock of client interface. @@ -53,72 +51,11 @@ func (m *Mockclient) EXPECT() *MockclientMockRecorder { return m.recorder } -// CreateOrUpdateAsync mocks base method. -func (m *Mockclient) CreateOrUpdateAsync(arg0 context.Context, arg1 azure0.ResourceSpecGetter, arg2 interface{}) (interface{}, azure.FutureAPI, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateOrUpdateAsync", arg0, arg1, arg2) - ret0, _ := ret[0].(interface{}) - ret1, _ := ret[1].(azure.FutureAPI) - ret2, _ := ret[2].(error) - return ret0, ret1, ret2 -} - -// CreateOrUpdateAsync indicates an expected call of CreateOrUpdateAsync. -func (mr *MockclientMockRecorder) CreateOrUpdateAsync(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrUpdateAsync", reflect.TypeOf((*Mockclient)(nil).CreateOrUpdateAsync), arg0, arg1, arg2) -} - -// DeleteAsync mocks base method. -func (m *Mockclient) DeleteAsync(arg0 context.Context, arg1 azure0.ResourceSpecGetter) (azure.FutureAPI, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "DeleteAsync", arg0, arg1) - ret0, _ := ret[0].(azure.FutureAPI) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// DeleteAsync indicates an expected call of DeleteAsync. -func (mr *MockclientMockRecorder) DeleteAsync(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAsync", reflect.TypeOf((*Mockclient)(nil).DeleteAsync), arg0, arg1) -} - -// Get mocks base method. -func (m *Mockclient) Get(arg0 context.Context, arg1 azure0.ResourceSpecGetter) (interface{}, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", arg0, arg1) - ret0, _ := ret[0].(interface{}) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get. -func (mr *MockclientMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*Mockclient)(nil).Get), arg0, arg1) -} - -// IsDone mocks base method. -func (m *Mockclient) IsDone(arg0 context.Context, arg1 azure.FutureAPI) (bool, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "IsDone", arg0, arg1) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// IsDone indicates an expected call of IsDone. -func (mr *MockclientMockRecorder) IsDone(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsDone", reflect.TypeOf((*Mockclient)(nil).IsDone), arg0, arg1) -} - // List mocks base method. -func (m *Mockclient) List(arg0 context.Context, arg1, arg2 string) ([]network.InboundNatRule, error) { +func (m *Mockclient) List(arg0 context.Context, arg1, arg2 string) ([]armnetwork.InboundNatRule, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "List", arg0, arg1, arg2) - ret0, _ := ret[0].([]network.InboundNatRule) + ret0, _ := ret[0].([]armnetwork.InboundNatRule) ret1, _ := ret[1].(error) return ret0, ret1 } @@ -128,18 +65,3 @@ func (mr *MockclientMockRecorder) List(arg0, arg1, arg2 interface{}) *gomock.Cal mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*Mockclient)(nil).List), arg0, arg1, arg2) } - -// Result mocks base method. -func (m *Mockclient) Result(arg0 context.Context, arg1 azure.FutureAPI, arg2 string) (interface{}, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Result", arg0, arg1, arg2) - ret0, _ := ret[0].(interface{}) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Result indicates an expected call of Result. -func (mr *MockclientMockRecorder) Result(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Result", reflect.TypeOf((*Mockclient)(nil).Result), arg0, arg1, arg2) -} diff --git a/azure/services/inboundnatrules/spec.go b/azure/services/inboundnatrules/spec.go index 4f1cc56ab03..6ee5c98c510 100644 --- a/azure/services/inboundnatrules/spec.go +++ b/azure/services/inboundnatrules/spec.go @@ -19,7 +19,7 @@ package inboundnatrules import ( "context" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-08-01/network" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" "github.com/pkg/errors" "k8s.io/utils/ptr" ) @@ -51,10 +51,10 @@ func (s *InboundNatSpec) OwnerResourceName() string { // Parameters returns the parameters for the inbound NAT rule. func (s *InboundNatSpec) Parameters(ctx context.Context, existing interface{}) (parameters interface{}, err error) { if existing != nil { - if _, ok := existing.(network.InboundNatRule); !ok { - return nil, errors.Errorf("%T is not a network.InboundNatRule", existing) + if _, ok := existing.(armnetwork.InboundNatRule); !ok { + return nil, errors.Errorf("%T is not an armnetwork.InboundNatRule", existing) } - + // Skip updating the existing inbound NAT rule return nil, nil } @@ -62,16 +62,16 @@ func (s *InboundNatSpec) Parameters(ctx context.Context, existing interface{}) ( return nil, errors.Errorf("FrontendIPConfigurationID is not set") } - rule := network.InboundNatRule{ + rule := armnetwork.InboundNatRule{ Name: ptr.To(s.ResourceName()), - InboundNatRulePropertiesFormat: &network.InboundNatRulePropertiesFormat{ + Properties: &armnetwork.InboundNatRulePropertiesFormat{ BackendPort: ptr.To[int32](22), EnableFloatingIP: ptr.To(false), IdleTimeoutInMinutes: ptr.To[int32](4), - FrontendIPConfiguration: &network.SubResource{ + FrontendIPConfiguration: &armnetwork.SubResource{ ID: s.FrontendIPConfigurationID, }, - Protocol: network.TransportProtocolTCP, + Protocol: ptr.To(armnetwork.TransportProtocolTCP), FrontendPort: s.SSHFrontendPort, }, } diff --git a/azure/services/inboundnatrules/spec_test.go b/azure/services/inboundnatrules/spec_test.go index 780fd61050e..1ed793598d7 100644 --- a/azure/services/inboundnatrules/spec_test.go +++ b/azure/services/inboundnatrules/spec_test.go @@ -17,11 +17,97 @@ limitations under the License. package inboundnatrules import ( + "context" + "reflect" "testing" + "github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4" + "github.com/google/go-cmp/cmp" . "github.com/onsi/gomega" + "k8s.io/utils/ptr" ) +func TestParameters(t *testing.T) { + testcases := []struct { + name string + spec InboundNatSpec + existing interface{} + expected interface{} + errorMsg string + }{ + { + name: "no existing InboundNatRule", + spec: fakeInboundNatSpec(true), + existing: nil, + expected: fakeNatRule(), + }, + { + name: "no existing InboundNatRule and FrontendIPConfigurationID not set", + spec: fakeInboundNatSpec(false), + existing: nil, + errorMsg: "FrontendIPConfigurationID is not set", + }, + { + name: "existing is not an InboundNatRule", + spec: fakeInboundNatSpec(true), + existing: context.TODO(), + errorMsg: "*context.emptyCtx is not an armnetwork.InboundNatRule", + }, + { + name: "existing InboundNatRule", + spec: fakeInboundNatSpec(false), + existing: fakeNatRule(), + expected: nil, + }, + } + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + t.Parallel() + + result, err := tc.spec.Parameters(context.Background(), tc.existing) + if tc.errorMsg != "" { + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring(tc.errorMsg)) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + if !reflect.DeepEqual(result, tc.expected) { + t.Errorf("Got difference between expected result and computed result:\n%s", cmp.Diff(tc.expected, result)) + } + }) + } +} + +func fakeInboundNatSpec(frontendIPConfigID bool) InboundNatSpec { + spec := InboundNatSpec{ + Name: "my-machine-1", + LoadBalancerName: "my-lb-1", + ResourceGroup: fakeGroupName, + } + if frontendIPConfigID { + spec.FrontendIPConfigurationID = ptr.To("frontend-ip-config-id-1") + } + return spec +} + +// fakeNatRule returns a fake InboundNatRule, associated with `fakeInboundNatSpec()`. +func fakeNatRule() armnetwork.InboundNatRule { + return armnetwork.InboundNatRule{ + Name: ptr.To("my-machine-1"), + Properties: &armnetwork.InboundNatRulePropertiesFormat{ + BackendPort: ptr.To[int32](22), + EnableFloatingIP: ptr.To(false), + FrontendIPConfiguration: &armnetwork.SubResource{ + ID: ptr.To("frontend-ip-config-id-1"), + }, + IdleTimeoutInMinutes: ptr.To[int32](4), + Protocol: ptr.To(armnetwork.TransportProtocolTCP), + }, + } +} + func TestGetAvailablePort(t *testing.T) { testcases := []struct { name string diff --git a/controllers/azuremachine_reconciler.go b/controllers/azuremachine_reconciler.go index 356b4e9a79b..9da575d7bd1 100644 --- a/controllers/azuremachine_reconciler.go +++ b/controllers/azuremachine_reconciler.go @@ -57,6 +57,10 @@ func newAzureMachineService(machineScope *scope.MachineScope) (*azureMachineServ if err != nil { return nil, errors.Wrap(err, "failed creating disks service") } + inboundnatrulesSvc, err := inboundnatrules.New(machineScope) + if err != nil { + return nil, errors.Wrap(err, "failed creating inboundnatrules service") + } vmextensionsSvc, err := vmextensions.New(machineScope) if err != nil { return nil, errors.Wrap(err, "failed creating vmextensions service") @@ -65,7 +69,7 @@ func newAzureMachineService(machineScope *scope.MachineScope) (*azureMachineServ scope: machineScope, services: []azure.ServiceReconciler{ publicips.New(machineScope), - inboundnatrules.New(machineScope), + inboundnatrulesSvc, networkinterfaces.New(machineScope, cache), availabilitysets.New(machineScope, cache), disksSvc,