diff --git a/api/v1beta1/conditions_consts.go b/api/v1beta1/conditions_consts.go index 0bb06c37d5f..775bc5a81b1 100644 --- a/api/v1beta1/conditions_consts.go +++ b/api/v1beta1/conditions_consts.go @@ -116,6 +116,8 @@ const ( RoleAssignmentReadyCondition clusterv1.ConditionType = "RoleAssignmentReady" // DisksReadyCondition means the disks exist and are ready to be used. DisksReadyCondition clusterv1.ConditionType = "DisksReady" + // NetworkInterfaceReadyCondition means the network interfaces exist and are ready to be used. + NetworkInterfaceReadyCondition clusterv1.ConditionType = "NetworkInterfacesReady" // CreatingReason means the resource is being created. CreatingReason = "Creating" diff --git a/azure/scope/machine.go b/azure/scope/machine.go index 1309c20443f..0d5bcc767db 100644 --- a/azure/scope/machine.go +++ b/azure/scope/machine.go @@ -41,6 +41,7 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/azure/services/availabilitysets" "sigs.k8s.io/cluster-api-provider-azure/azure/services/disks" "sigs.k8s.io/cluster-api-provider-azure/azure/services/inboundnatrules" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/networkinterfaces" "sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus" "sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualmachines" "sigs.k8s.io/cluster-api-provider-azure/util/futures" @@ -216,18 +217,21 @@ func (m *MachineScope) InboundNatSpecs(portsInUse map[int32]struct{}) []azure.Re } // NICSpecs returns the network interface specs. -func (m *MachineScope) NICSpecs() []azure.NICSpec { - spec := azure.NICSpec{ +func (m *MachineScope) NICSpecs() []azure.ResourceSpecGetter { + spec := &networkinterfaces.NICSpec{ Name: azure.GenerateNICName(m.Name()), + ResourceGroup: m.ResourceGroup(), + Location: m.Location(), + SubscriptionID: m.SubscriptionID(), MachineName: m.Name(), VNetName: m.Vnet().Name, VNetResourceGroup: m.Vnet().ResourceGroup, SubnetName: m.AzureMachine.Spec.SubnetName, - VMSize: m.AzureMachine.Spec.VMSize, AcceleratedNetworking: m.AzureMachine.Spec.AcceleratedNetworking, IPv6Enabled: m.IsIPv6Enabled(), EnableIPForwarding: m.AzureMachine.Spec.EnableIPForwarding, } + if m.Role() == infrav1.ControlPlane { spec.PublicLBName = m.OutboundLBName(m.Role()) spec.PublicLBAddressPoolName = m.OutboundPoolName(m.OutboundLBName(m.Role())) @@ -250,7 +254,11 @@ func (m *MachineScope) NICSpecs() []azure.NICSpec { spec.PublicIPName = azure.GenerateNodePublicIPName(m.Name()) } - return []azure.NICSpec{spec} + if m.cache != nil { + spec.SKU = &m.cache.VMSKU + } + + return []azure.ResourceSpecGetter{spec} } // NICIDs returns the NIC resource IDs. @@ -258,7 +266,7 @@ func (m *MachineScope) NICIDs() []string { nicspecs := m.NICSpecs() nicIDs := make([]string, len(nicspecs)) for i, nic := range nicspecs { - nicIDs[i] = azure.NetworkInterfaceID(m.SubscriptionID(), m.ResourceGroup(), nic.Name) + nicIDs[i] = azure.NetworkInterfaceID(m.SubscriptionID(), nic.ResourceGroupName(), nic.ResourceName()) } return nicIDs } @@ -544,6 +552,7 @@ func (m *MachineScope) PatchObject(ctx context.Context) error { conditions.WithConditions( infrav1.VMRunningCondition, infrav1.AvailabilitySetReadyCondition, + infrav1.NetworkInterfaceReadyCondition, ), ) @@ -554,6 +563,7 @@ func (m *MachineScope) PatchObject(ctx context.Context) error { clusterv1.ReadyCondition, infrav1.VMRunningCondition, infrav1.AvailabilitySetReadyCondition, + infrav1.NetworkInterfaceReadyCondition, }}) } diff --git a/azure/scope/machine_test.go b/azure/scope/machine_test.go index ed2219329b3..159ea49fd8a 100644 --- a/azure/scope/machine_test.go +++ b/azure/scope/machine_test.go @@ -35,6 +35,8 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/azure" "sigs.k8s.io/cluster-api-provider-azure/azure/services/disks" "sigs.k8s.io/cluster-api-provider-azure/azure/services/inboundnatrules" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/networkinterfaces" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus" ) func specArrayToString(specs []azure.ResourceSpecGetter) string { @@ -1336,12 +1338,19 @@ func TestMachineScope_NICSpecs(t *testing.T) { tests := []struct { name string machineScope MachineScope - want []azure.NICSpec + want []azure.ResourceSpecGetter }{ { name: "Node Machine with no NAT gateway and no public IP address", machineScope: MachineScope{ ClusterScoper: &ClusterScope{ + AzureClients: AzureClients{ + EnvironmentSettings: auth.EnvironmentSettings{ + Values: map[string]string{ + auth.SubscriptionID: "123", + }, + }, + }, Cluster: &clusterv1.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -1361,6 +1370,8 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, Spec: infrav1.AzureClusterSpec{ + ResourceGroup: "my-rg", + Location: "westus", NetworkSpec: infrav1.NetworkSpec{ Vnet: infrav1.VnetSpec{ Name: "vnet1", @@ -1397,9 +1408,108 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, }, - want: []azure.NICSpec{ - { + want: []azure.ResourceSpecGetter{ + &networkinterfaces.NICSpec{ + Name: "machine-name-nic", + ResourceGroup: "my-rg", + Location: "westus", + SubscriptionID: "123", + MachineName: "machine-name", + SubnetName: "subnet1", + VNetName: "vnet1", + VNetResourceGroup: "rg1", + PublicLBName: "outbound-lb", + PublicLBAddressPoolName: "outbound-lb-outboundBackendPool", + PublicLBNATRuleName: "", + InternalLBName: "", + InternalLBAddressPoolName: "", + PublicIPName: "", + AcceleratedNetworking: nil, + IPv6Enabled: false, + EnableIPForwarding: false, + SKU: nil, + }, + }, + }, + { + name: "Node Machine with no NAT gateway and no public IP address and SKU is in machine cache", + machineScope: MachineScope{ + ClusterScoper: &ClusterScope{ + AzureClients: AzureClients{ + EnvironmentSettings: auth.EnvironmentSettings{ + Values: map[string]string{ + auth.SubscriptionID: "123", + }, + }, + }, + Cluster: &clusterv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + Namespace: "default", + }, + }, + AzureCluster: &infrav1.AzureCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster", + Namespace: "default", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: "cluster.x-k8s.io/v1beta1", + Kind: "Cluster", + Name: "cluster", + }, + }, + }, + Spec: infrav1.AzureClusterSpec{ + ResourceGroup: "my-rg", + Location: "westus", + NetworkSpec: infrav1.NetworkSpec{ + Vnet: infrav1.VnetSpec{ + Name: "vnet1", + ResourceGroup: "rg1", + }, + Subnets: []infrav1.SubnetSpec{ + { + Role: infrav1.SubnetNode, + Name: "subnet1", + }, + }, + NodeOutboundLB: &infrav1.LoadBalancerSpec{ + Name: "outbound-lb", + }, + }, + }, + }, + }, + AzureMachine: &infrav1.AzureMachine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "machine", + }, + Spec: infrav1.AzureMachineSpec{ + ProviderID: to.StringPtr("azure://compute/virtual-machines/machine-name"), + SubnetName: "subnet1", + }, + }, + Machine: &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: "machine", + Labels: map[string]string{ + //clusterv1.MachineControlPlaneLabelName: "true", + }, + }, + }, + cache: &MachineCache{ + VMSKU: resourceskus.SKU{ + Name: to.StringPtr("Standard_D2v2"), + }, + }, + }, + want: []azure.ResourceSpecGetter{ + &networkinterfaces.NICSpec{ Name: "machine-name-nic", + ResourceGroup: "my-rg", + Location: "westus", + SubscriptionID: "123", MachineName: "machine-name", SubnetName: "subnet1", VNetName: "vnet1", @@ -1410,10 +1520,12 @@ func TestMachineScope_NICSpecs(t *testing.T) { InternalLBName: "", InternalLBAddressPoolName: "", PublicIPName: "", - VMSize: "", AcceleratedNetworking: nil, IPv6Enabled: false, EnableIPForwarding: false, + SKU: &resourceskus.SKU{ + Name: to.StringPtr("Standard_D2v2"), + }, }, }, }, @@ -1421,6 +1533,13 @@ func TestMachineScope_NICSpecs(t *testing.T) { name: "Node Machine with NAT gateway", machineScope: MachineScope{ ClusterScoper: &ClusterScope{ + AzureClients: AzureClients{ + EnvironmentSettings: auth.EnvironmentSettings{ + Values: map[string]string{ + auth.SubscriptionID: "123", + }, + }, + }, Cluster: &clusterv1.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -1440,6 +1559,8 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, Spec: infrav1.AzureClusterSpec{ + ResourceGroup: "my-rg", + Location: "westus", NetworkSpec: infrav1.NetworkSpec{ Vnet: infrav1.VnetSpec{ Name: "vnet1", @@ -1479,9 +1600,12 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, }, - want: []azure.NICSpec{ - { + want: []azure.ResourceSpecGetter{ + &networkinterfaces.NICSpec{ Name: "machine-name-nic", + ResourceGroup: "my-rg", + Location: "westus", + SubscriptionID: "123", MachineName: "machine-name", SubnetName: "subnet1", VNetName: "vnet1", @@ -1492,10 +1616,10 @@ func TestMachineScope_NICSpecs(t *testing.T) { InternalLBName: "", InternalLBAddressPoolName: "", PublicIPName: "", - VMSize: "", AcceleratedNetworking: nil, IPv6Enabled: false, EnableIPForwarding: false, + SKU: nil, }, }, }, @@ -1503,6 +1627,13 @@ func TestMachineScope_NICSpecs(t *testing.T) { name: "Node Machine with public IP address", machineScope: MachineScope{ ClusterScoper: &ClusterScope{ + AzureClients: AzureClients{ + EnvironmentSettings: auth.EnvironmentSettings{ + Values: map[string]string{ + auth.SubscriptionID: "123", + }, + }, + }, Cluster: &clusterv1.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -1522,6 +1653,8 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, Spec: infrav1.AzureClusterSpec{ + ResourceGroup: "my-rg", + Location: "westus", NetworkSpec: infrav1.NetworkSpec{ Vnet: infrav1.VnetSpec{ Name: "vnet1", @@ -1559,9 +1692,12 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, }, - want: []azure.NICSpec{ - { + want: []azure.ResourceSpecGetter{ + &networkinterfaces.NICSpec{ Name: "machine-name-nic", + ResourceGroup: "my-rg", + Location: "westus", + SubscriptionID: "123", MachineName: "machine-name", SubnetName: "subnet1", VNetName: "vnet1", @@ -1572,10 +1708,10 @@ func TestMachineScope_NICSpecs(t *testing.T) { InternalLBName: "", InternalLBAddressPoolName: "", PublicIPName: "pip-machine-name", - VMSize: "", AcceleratedNetworking: nil, IPv6Enabled: false, EnableIPForwarding: false, + SKU: nil, }, }, }, @@ -1583,6 +1719,13 @@ func TestMachineScope_NICSpecs(t *testing.T) { name: "Control Plane Machine with private LB", machineScope: MachineScope{ ClusterScoper: &ClusterScope{ + AzureClients: AzureClients{ + EnvironmentSettings: auth.EnvironmentSettings{ + Values: map[string]string{ + auth.SubscriptionID: "123", + }, + }, + }, Cluster: &clusterv1.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -1602,6 +1745,8 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, Spec: infrav1.AzureClusterSpec{ + ResourceGroup: "my-rg", + Location: "westus", NetworkSpec: infrav1.NetworkSpec{ Vnet: infrav1.VnetSpec{ Name: "vnet1", @@ -1642,9 +1787,12 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, }, - want: []azure.NICSpec{ - { + want: []azure.ResourceSpecGetter{ + &networkinterfaces.NICSpec{ Name: "machine-name-nic", + ResourceGroup: "my-rg", + Location: "westus", + SubscriptionID: "123", MachineName: "machine-name", SubnetName: "subnet1", VNetName: "vnet1", @@ -1655,10 +1803,10 @@ func TestMachineScope_NICSpecs(t *testing.T) { InternalLBName: "api-lb", InternalLBAddressPoolName: "api-lb-backendPool", PublicIPName: "", - VMSize: "", AcceleratedNetworking: nil, IPv6Enabled: false, EnableIPForwarding: false, + SKU: nil, }, }, }, @@ -1666,6 +1814,13 @@ func TestMachineScope_NICSpecs(t *testing.T) { name: "Control Plane Machine with public LB", machineScope: MachineScope{ ClusterScoper: &ClusterScope{ + AzureClients: AzureClients{ + EnvironmentSettings: auth.EnvironmentSettings{ + Values: map[string]string{ + auth.SubscriptionID: "123", + }, + }, + }, Cluster: &clusterv1.Cluster{ ObjectMeta: metav1.ObjectMeta{ Name: "cluster", @@ -1685,6 +1840,8 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, Spec: infrav1.AzureClusterSpec{ + ResourceGroup: "my-rg", + Location: "westus", NetworkSpec: infrav1.NetworkSpec{ Vnet: infrav1.VnetSpec{ Name: "vnet1", @@ -1724,9 +1881,12 @@ func TestMachineScope_NICSpecs(t *testing.T) { }, }, }, - want: []azure.NICSpec{ - { + want: []azure.ResourceSpecGetter{ + &networkinterfaces.NICSpec{ Name: "machine-name-nic", + ResourceGroup: "my-rg", + Location: "westus", + SubscriptionID: "123", MachineName: "machine-name", SubnetName: "subnet1", VNetName: "vnet1", @@ -1737,10 +1897,10 @@ func TestMachineScope_NICSpecs(t *testing.T) { InternalLBName: "", InternalLBAddressPoolName: "", PublicIPName: "", - VMSize: "", AcceleratedNetworking: nil, IPv6Enabled: false, EnableIPForwarding: false, + SKU: nil, }, }, }, @@ -1749,7 +1909,7 @@ func TestMachineScope_NICSpecs(t *testing.T) { t.Run(tt.name, func(t *testing.T) { gotNicSpecs := tt.machineScope.NICSpecs() if !reflect.DeepEqual(gotNicSpecs, tt.want) { - t.Errorf("NICSpecs(), gotNicSpecs = %v, want %v", gotNicSpecs, tt.want) + t.Errorf("NICSpecs(), gotNicSpecs = %s, want %s", specArrayToString(gotNicSpecs), specArrayToString(tt.want)) } }) } diff --git a/azure/services/networkinterfaces/client.go b/azure/services/networkinterfaces/client.go index 761e2fa1ba1..0d4d5f565c2 100644 --- a/azure/services/networkinterfaces/client.go +++ b/azure/services/networkinterfaces/client.go @@ -18,25 +18,34 @@ package networkinterfaces import ( "context" + "encoding/json" "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network" "github.com/Azure/go-autorest/autorest" + 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" ) -// Client wraps go-sdk. -type Client interface { - Get(context.Context, string, string) (network.Interface, error) - CreateOrUpdate(context.Context, string, string, network.Interface) error - Delete(context.Context, string, string) error -} +type ( + // Client wraps go-sdk. + Client interface { + 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) + } -// AzureClient contains the Azure go-sdk Client. -type AzureClient struct { - interfaces network.InterfacesClient -} + // AzureClient contains the Azure go-sdk Client. + AzureClient struct { + interfaces network.InterfacesClient + } +) var _ Client = &AzureClient{} @@ -53,44 +62,114 @@ func newInterfacesClient(subscriptionID string, baseURI string, authorizer autor return nicClient } -// Get gets information about the specified network interface. -func (ac *AzureClient) Get(ctx context.Context, resourceGroupName, nicName string) (network.Interface, error) { +// Get gets the specified network interface. +func (ac *AzureClient) Get(ctx context.Context, spec azure.ResourceSpecGetter) (result interface{}, err error) { ctx, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.AzureClient.Get") defer done() - return ac.interfaces.Get(ctx, resourceGroupName, nicName, "") + return ac.interfaces.Get(ctx, spec.ResourceGroupName(), spec.ResourceName(), "") } -// CreateOrUpdate creates or updates a network interface. -func (ac *AzureClient) CreateOrUpdate(ctx context.Context, resourceGroupName string, nicName string, nic network.Interface) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.AzureClient.CreateOrUpdate") +// CreateOrUpdateAsync creates or updates a network interface 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 (ac *AzureClient) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, parameters interface{}) (result interface{}, future azureautorest.FutureAPI, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.AzureClient.CreateOrUpdateAsync") defer done() - future, err := ac.interfaces.CreateOrUpdate(ctx, resourceGroupName, nicName, nic) + networkInterface, ok := parameters.(network.Interface) + if !ok { + return nil, nil, errors.Errorf("%T is not a network.Interface", parameters) + } + + createFuture, err := ac.interfaces.CreateOrUpdate(ctx, spec.ResourceGroupName(), spec.ResourceName(), networkInterface) if err != nil { - return err + return nil, nil, err } - err = future.WaitForCompletionRef(ctx, ac.interfaces.Client) + + ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureCallTimeout) + defer cancel() + + err = createFuture.WaitForCompletionRef(ctx, ac.interfaces.Client) if err != nil { - return err + // if an error occurs, return the future. + // this means the long-running operation didn't finish in the specified timeout. + return nil, &createFuture, err } - _, err = future.Result(ac.interfaces) - return err + + result, err = createFuture.Result(ac.interfaces) + // if the operation completed, return a nil future + return result, nil, err } -// Delete deletes the specified network interface. -func (ac *AzureClient) Delete(ctx context.Context, resourceGroupName, nicName string) error { - ctx, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.AzureClient.Delete") +// DeleteAsync deletes a network interface 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 (ac *AzureClient) DeleteAsync(ctx context.Context, spec azure.ResourceSpecGetter) (future azureautorest.FutureAPI, err error) { + ctx, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.AzureClient.DeleteAsync") defer done() - future, err := ac.interfaces.Delete(ctx, resourceGroupName, nicName) + deleteFuture, err := ac.interfaces.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, ac.interfaces.Client) if err != nil { - return err + // if an error occurs, return the future. + // this means the long-running operation didn't finish in the specified timeout. + return &deleteFuture, err } - err = future.WaitForCompletionRef(ctx, ac.interfaces.Client) + _, err = deleteFuture.Result(ac.interfaces) + // if the operation completed, return a nil future. + 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, "networkinterfaces.AzureClient.IsDone") + defer done() + + isDone, err = future.DoneWithContext(ctx, ac.interfaces) if err != nil { - return err + 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 (ac *AzureClient) Result(ctx context.Context, future azureautorest.FutureAPI, futureType string) (result interface{}, err error) { + _, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.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 InterfacesCreateOrUpdateFuture 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.InterfacesCreateOrUpdateFuture + 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.interfaces) + + case infrav1.DeleteFuture: + // Delete does not return a result network interface + return nil, nil + + default: + return nil, errors.Errorf("unknown future type %q", futureType) } - _, err = future.Result(ac.interfaces) - return err } diff --git a/azure/services/networkinterfaces/mock_networkinterfaces/client_mock.go b/azure/services/networkinterfaces/mock_networkinterfaces/client_mock.go index d0e5a8bca4c..0b57ab7f230 100644 --- a/azure/services/networkinterfaces/mock_networkinterfaces/client_mock.go +++ b/azure/services/networkinterfaces/mock_networkinterfaces/client_mock.go @@ -24,8 +24,9 @@ import ( context "context" reflect "reflect" - network "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network" + azure "github.com/Azure/go-autorest/autorest/azure" gomock "github.com/golang/mock/gomock" + azure0 "sigs.k8s.io/cluster-api-provider-azure/azure" ) // MockClient is a mock of Client interface. @@ -51,45 +52,78 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } -// CreateOrUpdate mocks base method. -func (m *MockClient) CreateOrUpdate(arg0 context.Context, arg1, arg2 string, arg3 network.Interface) error { +// 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, "CreateOrUpdate", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 + 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 } -// CreateOrUpdate indicates an expected call of CreateOrUpdate. -func (mr *MockClientMockRecorder) CreateOrUpdate(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +// 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, "CreateOrUpdate", reflect.TypeOf((*MockClient)(nil).CreateOrUpdate), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateOrUpdateAsync", reflect.TypeOf((*MockClient)(nil).CreateOrUpdateAsync), arg0, arg1, arg2) } -// Delete mocks base method. -func (m *MockClient) Delete(arg0 context.Context, arg1, arg2 string) error { +// 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, "Delete", arg0, arg1, arg2) - ret0, _ := ret[0].(error) - return ret0 + ret := m.ctrl.Call(m, "DeleteAsync", arg0, arg1) + ret0, _ := ret[0].(azure.FutureAPI) + ret1, _ := ret[1].(error) + return ret0, ret1 } -// Delete indicates an expected call of Delete. -func (mr *MockClientMockRecorder) Delete(arg0, arg1, arg2 interface{}) *gomock.Call { +// 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, "Delete", reflect.TypeOf((*MockClient)(nil).Delete), arg0, arg1, arg2) + 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, arg2 string) (network.Interface, error) { +func (m *MockClient) Get(arg0 context.Context, arg1 azure0.ResourceSpecGetter) (interface{}, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", arg0, arg1, arg2) - ret0, _ := ret[0].(network.Interface) + 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, arg2 interface{}) *gomock.Call { +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) +} + +// 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, "Get", reflect.TypeOf((*MockClient)(nil).Get), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Result", reflect.TypeOf((*MockClient)(nil).Result), arg0, arg1, arg2) } diff --git a/azure/services/networkinterfaces/mock_networkinterfaces/networkinterfaces_mock.go b/azure/services/networkinterfaces/mock_networkinterfaces/networkinterfaces_mock.go index 1b36d6c0e5e..0abcd0143ee 100644 --- a/azure/services/networkinterfaces/mock_networkinterfaces/networkinterfaces_mock.go +++ b/azure/services/networkinterfaces/mock_networkinterfaces/networkinterfaces_mock.go @@ -27,6 +27,7 @@ import ( gomock "github.com/golang/mock/gomock" v1beta1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" azure "sigs.k8s.io/cluster-api-provider-azure/azure" + v1beta10 "sigs.k8s.io/cluster-api/api/v1beta1" ) // MockNICScope is a mock of NICScope interface. @@ -178,6 +179,18 @@ func (mr *MockNICScopeMockRecorder) ClusterName() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ClusterName", reflect.TypeOf((*MockNICScope)(nil).ClusterName)) } +// DeleteLongRunningOperationState mocks base method. +func (m *MockNICScope) DeleteLongRunningOperationState(arg0, arg1 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "DeleteLongRunningOperationState", arg0, arg1) +} + +// DeleteLongRunningOperationState indicates an expected call of DeleteLongRunningOperationState. +func (mr *MockNICScopeMockRecorder) DeleteLongRunningOperationState(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteLongRunningOperationState", reflect.TypeOf((*MockNICScope)(nil).DeleteLongRunningOperationState), arg0, arg1) +} + // FailureDomains mocks base method. func (m *MockNICScope) FailureDomains() []string { m.ctrl.T.Helper() @@ -192,6 +205,20 @@ func (mr *MockNICScopeMockRecorder) FailureDomains() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FailureDomains", reflect.TypeOf((*MockNICScope)(nil).FailureDomains)) } +// GetLongRunningOperationState mocks base method. +func (m *MockNICScope) GetLongRunningOperationState(arg0, arg1 string) *v1beta1.Future { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLongRunningOperationState", arg0, arg1) + ret0, _ := ret[0].(*v1beta1.Future) + return ret0 +} + +// GetLongRunningOperationState indicates an expected call of GetLongRunningOperationState. +func (mr *MockNICScopeMockRecorder) GetLongRunningOperationState(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLongRunningOperationState", reflect.TypeOf((*MockNICScope)(nil).GetLongRunningOperationState), arg0, arg1) +} + // HashKey mocks base method. func (m *MockNICScope) HashKey() string { m.ctrl.T.Helper() @@ -221,10 +248,10 @@ func (mr *MockNICScopeMockRecorder) Location() *gomock.Call { } // NICSpecs mocks base method. -func (m *MockNICScope) NICSpecs() []azure.NICSpec { +func (m *MockNICScope) NICSpecs() []azure.ResourceSpecGetter { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "NICSpecs") - ret0, _ := ret[0].([]azure.NICSpec) + ret0, _ := ret[0].([]azure.ResourceSpecGetter) return ret0 } @@ -248,6 +275,18 @@ func (mr *MockNICScopeMockRecorder) ResourceGroup() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ResourceGroup", reflect.TypeOf((*MockNICScope)(nil).ResourceGroup)) } +// SetLongRunningOperationState mocks base method. +func (m *MockNICScope) SetLongRunningOperationState(arg0 *v1beta1.Future) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetLongRunningOperationState", arg0) +} + +// SetLongRunningOperationState indicates an expected call of SetLongRunningOperationState. +func (mr *MockNICScopeMockRecorder) SetLongRunningOperationState(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLongRunningOperationState", reflect.TypeOf((*MockNICScope)(nil).SetLongRunningOperationState), arg0) +} + // SubscriptionID mocks base method. func (m *MockNICScope) SubscriptionID() string { m.ctrl.T.Helper() @@ -275,3 +314,39 @@ func (mr *MockNICScopeMockRecorder) TenantID() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TenantID", reflect.TypeOf((*MockNICScope)(nil).TenantID)) } + +// UpdateDeleteStatus mocks base method. +func (m *MockNICScope) UpdateDeleteStatus(arg0 v1beta10.ConditionType, arg1 string, arg2 error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UpdateDeleteStatus", arg0, arg1, arg2) +} + +// UpdateDeleteStatus indicates an expected call of UpdateDeleteStatus. +func (mr *MockNICScopeMockRecorder) UpdateDeleteStatus(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateDeleteStatus", reflect.TypeOf((*MockNICScope)(nil).UpdateDeleteStatus), arg0, arg1, arg2) +} + +// UpdatePatchStatus mocks base method. +func (m *MockNICScope) UpdatePatchStatus(arg0 v1beta10.ConditionType, arg1 string, arg2 error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UpdatePatchStatus", arg0, arg1, arg2) +} + +// UpdatePatchStatus indicates an expected call of UpdatePatchStatus. +func (mr *MockNICScopeMockRecorder) UpdatePatchStatus(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePatchStatus", reflect.TypeOf((*MockNICScope)(nil).UpdatePatchStatus), arg0, arg1, arg2) +} + +// UpdatePutStatus mocks base method. +func (m *MockNICScope) UpdatePutStatus(arg0 v1beta10.ConditionType, arg1 string, arg2 error) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "UpdatePutStatus", arg0, arg1, arg2) +} + +// UpdatePutStatus indicates an expected call of UpdatePutStatus. +func (mr *MockNICScopeMockRecorder) UpdatePutStatus(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePutStatus", reflect.TypeOf((*MockNICScope)(nil).UpdatePutStatus), arg0, arg1, arg2) +} diff --git a/azure/services/networkinterfaces/networkinterfaces.go b/azure/services/networkinterfaces/networkinterfaces.go index 62a6703c81e..bc956ccbf5f 100644 --- a/azure/services/networkinterfaces/networkinterfaces.go +++ b/azure/services/networkinterfaces/networkinterfaces.go @@ -19,158 +19,84 @@ package networkinterfaces import ( "context" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network" - "github.com/Azure/go-autorest/autorest/to" - "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/resourceskus" + "sigs.k8s.io/cluster-api-provider-azure/util/reconciler" "sigs.k8s.io/cluster-api-provider-azure/util/tele" ) +const serviceName = "interfaces" + // NICScope defines the scope interface for a network interfaces service. type NICScope interface { azure.ClusterDescriber - NICSpecs() []azure.NICSpec + azure.AsyncStatusUpdater + NICSpecs() []azure.ResourceSpecGetter } // Service provides operations on Azure resources. type Service struct { Scope NICScope - Client + async.Reconciler resourceSKUCache *resourceskus.Cache } // New creates a new service. func New(scope NICScope, skuCache *resourceskus.Cache) *Service { + Client := NewClient(scope) return &Service{ Scope: scope, - Client: NewClient(scope), + Reconciler: async.New(scope, Client, Client), resourceSKUCache: skuCache, } } // Reconcile gets/creates/updates a network interface. func (s *Service) Reconcile(ctx context.Context) error { - ctx, log, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.Service.Reconcile") + ctx, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.Service.Reconcile") defer done() - for _, nicSpec := range s.Scope.NICSpecs() { - _, err := s.Client.Get(ctx, s.Scope.ResourceGroup(), nicSpec.Name) - switch { - case err != nil && !azure.ResourceNotFound(err): - return errors.Wrapf(err, "failed to fetch network interface %s", nicSpec.Name) - case err == nil: - // network interface already exists, do nothing - continue - default: - nicConfig := &network.InterfaceIPConfigurationPropertiesFormat{} - - subnet := &network.Subnet{ - ID: to.StringPtr(azure.SubnetID(s.Scope.SubscriptionID(), nicSpec.VNetResourceGroup, nicSpec.VNetName, nicSpec.SubnetName)), - } - nicConfig.Subnet = subnet - - nicConfig.PrivateIPAllocationMethod = network.IPAllocationMethodDynamic - if nicSpec.StaticIPAddress != "" { - nicConfig.PrivateIPAllocationMethod = network.IPAllocationMethodStatic - nicConfig.PrivateIPAddress = to.StringPtr(nicSpec.StaticIPAddress) - } - - backendAddressPools := []network.BackendAddressPool{} - if nicSpec.PublicLBName != "" { - if nicSpec.PublicLBAddressPoolName != "" { - backendAddressPools = append(backendAddressPools, - network.BackendAddressPool{ - ID: to.StringPtr(azure.AddressPoolID(s.Scope.SubscriptionID(), s.Scope.ResourceGroup(), nicSpec.PublicLBName, nicSpec.PublicLBAddressPoolName)), - }) - } - if nicSpec.PublicLBNATRuleName != "" { - nicConfig.LoadBalancerInboundNatRules = &[]network.InboundNatRule{ - { - ID: to.StringPtr(azure.NATRuleID(s.Scope.SubscriptionID(), s.Scope.ResourceGroup(), nicSpec.PublicLBName, nicSpec.PublicLBNATRuleName)), - }, - } - } - } - if nicSpec.InternalLBName != "" && nicSpec.InternalLBAddressPoolName != "" { - backendAddressPools = append(backendAddressPools, - network.BackendAddressPool{ - ID: to.StringPtr(azure.AddressPoolID(s.Scope.SubscriptionID(), s.Scope.ResourceGroup(), nicSpec.InternalLBName, nicSpec.InternalLBAddressPoolName)), - }) - } - nicConfig.LoadBalancerBackendAddressPools = &backendAddressPools - - if nicSpec.PublicIPName != "" { - nicConfig.PublicIPAddress = &network.PublicIPAddress{ - ID: to.StringPtr(azure.PublicIPID(s.Scope.SubscriptionID(), s.Scope.ResourceGroup(), nicSpec.PublicIPName)), - } - } - - if nicSpec.AcceleratedNetworking == nil { - // set accelerated networking to the capability of the VMSize - sku, err := s.resourceSKUCache.Get(ctx, nicSpec.VMSize, resourceskus.VirtualMachines) - if err != nil { - return azure.WithTerminalError(errors.Wrapf(err, "failed to get SKU %s in compute api", nicSpec.VMSize)) - } + ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureServiceReconcileTimeout) + defer cancel() - accelNet := sku.HasCapability(resourceskus.AcceleratedNetworking) - nicSpec.AcceleratedNetworking = &accelNet - } - - ipConfigurations := []network.InterfaceIPConfiguration{ - { - Name: to.StringPtr("pipConfig"), - InterfaceIPConfigurationPropertiesFormat: nicConfig, - }, - } - - if nicSpec.IPv6Enabled { - ipv6Config := network.InterfaceIPConfiguration{ - Name: to.StringPtr("ipConfigv6"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - PrivateIPAddressVersion: "IPv6", - Primary: to.BoolPtr(false), - Subnet: &network.Subnet{ID: subnet.ID}, - }, - } - - ipConfigurations = append(ipConfigurations, ipv6Config) - } - - err = s.Client.CreateOrUpdate(ctx, - s.Scope.ResourceGroup(), - nicSpec.Name, - network.Interface{ - Location: to.StringPtr(s.Scope.Location()), - InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ - EnableAcceleratedNetworking: nicSpec.AcceleratedNetworking, - IPConfigurations: &ipConfigurations, - EnableIPForwarding: to.BoolPtr(nicSpec.EnableIPForwarding), - }, - }) - - if err != nil { - return errors.Wrapf(err, "failed to create network interface %s in resource group %s", nicSpec.Name, s.Scope.ResourceGroup()) + // We go through the list of NICSpecs to reconcile each one, independently of the result of the previous one. + // If multiple errors occur, we return the most pressing one. + // Order of precedence (highest -> lowest) is: error that is not an operationNotDoneError (i.e. error creating) -> operationNotDoneError (i.e. creating in progress) -> no error (i.e. created) + var result error + for _, nicSpec := range s.Scope.NICSpecs() { + if _, err := s.CreateResource(ctx, nicSpec, serviceName); err != nil { + if !azure.IsOperationNotDoneError(err) || result == nil { + result = err } - log.V(2).Info("successfully created network interface", "network interface", nicSpec.Name) } } - return nil + + s.Scope.UpdatePutStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, result) + return result } // Delete deletes the network interface with the provided name. func (s *Service) Delete(ctx context.Context) error { - ctx, log, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.Service.Delete") + ctx, _, done := tele.StartSpanWithLogger(ctx, "networkinterfaces.Service.Delete") defer done() + ctx, cancel := context.WithTimeout(ctx, reconciler.DefaultAzureServiceReconcileTimeout) + defer cancel() + + var result error + + // We go through the list of NICSpecs to delete each one, independently of the result of the previous one. + // If multiple errors occur, we return the most pressing one. + // Order of precedence (highest -> lowest) is: error that is not an operationNotDoneError (i.e. error deleting) -> operationNotDoneError (i.e. deleting in progress) -> no error (i.e. deleted) for _, nicSpec := range s.Scope.NICSpecs() { - log.V(2).Info("deleting network interface", "network interface", nicSpec.Name) - err := s.Client.Delete(ctx, s.Scope.ResourceGroup(), nicSpec.Name) - if err != nil && !azure.ResourceNotFound(err) { - return errors.Wrapf(err, "failed to delete network interface %s in resource group %s", nicSpec.Name, s.Scope.ResourceGroup()) + if err := s.DeleteResource(ctx, nicSpec, serviceName); err != nil { + if !azure.IsOperationNotDoneError(err) || result == nil { + result = err + } } - log.V(2).Info("successfully deleted NIC", "network interface", nicSpec.Name) } - return nil + s.Scope.UpdateDeleteStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, result) + return result } diff --git a/azure/services/networkinterfaces/networkinterfaces_test.go b/azure/services/networkinterfaces/networkinterfaces_test.go index 8cda24560fa..c1dc657801a 100644 --- a/azure/services/networkinterfaces/networkinterfaces_test.go +++ b/azure/services/networkinterfaces/networkinterfaces_test.go @@ -22,368 +22,78 @@ import ( "net/http" "testing" - "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute" - "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network" "github.com/Azure/go-autorest/autorest" - "github.com/Azure/go-autorest/autorest/to" "github.com/golang/mock/gomock" "github.com/google/go-cmp/cmp" . "github.com/onsi/gomega" + 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/mock_async" "sigs.k8s.io/cluster-api-provider-azure/azure/services/networkinterfaces/mock_networkinterfaces" - "sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus" gomockinternal "sigs.k8s.io/cluster-api-provider-azure/internal/test/matchers/gomock" ) +var ( + fakeNICSpec1 = NICSpec{ + Name: "nic-1", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + AcceleratedNetworking: nil, + SKU: &fakeSku, + } + fakeNICSpec2 = NICSpec{ + Name: "nic-2", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + AcceleratedNetworking: nil, + SKU: &fakeSku, + } + internalError = autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error") +) + func TestReconcileNetworkInterface(t *testing.T) { testcases := []struct { name string expectedError string - expect func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) + expect func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) }{ { - name: "network interface already exists", - expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "nic-1", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - VMSize: "Standard_D2v2", - }, - { - Name: "nic-2", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - VMSize: "Standard_D2v2", - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - gomock.InOrder( - m.Get(gomockinternal.AContext(), "my-rg", "nic-1"), - m.Get(gomockinternal.AContext(), "my-rg", "nic-2")) - }, - }, - { - name: "node network interface create fails", - expectedError: "failed to create network interface my-net-interface in resource group my-rg: #: Internal Server Error: StatusCode=500", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - PublicLBName: "my-public-lb", - VMSize: "Standard_D2v2", - AcceleratedNetworking: nil, - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - gomock.InOrder( - m.Get(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")), - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-net-interface", gomock.AssignableToTypeOf(network.Interface{})). - Return(autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error"))) - }, - }, - { - name: "node network interface with Static private IP successfully created", - expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - PublicLBName: "my-public-lb", - PublicLBAddressPoolName: "cluster-name-outboundBackendPool", - StaticIPAddress: "fake.static.ip", - VMSize: "Standard_D2v2", - AcceleratedNetworking: nil, - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - m.Get(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ - Location: to.StringPtr("fake-location"), - InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ - EnableAcceleratedNetworking: to.BoolPtr(true), - EnableIPForwarding: to.BoolPtr(false), - IPConfigurations: &[]network.InterfaceIPConfiguration{ - { - Name: to.StringPtr("pipConfig"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/backendAddressPools/cluster-name-outboundBackendPool")}}, - PrivateIPAllocationMethod: network.IPAllocationMethodStatic, - PrivateIPAddress: to.StringPtr("fake.static.ip"), - Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, - }, - }, - }, - }, - })) - }, - }, - { - name: "node network interface with Dynamic private IP successfully created", - expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - PublicLBName: "my-public-lb", - PublicLBAddressPoolName: "cluster-name-outboundBackendPool", - VMSize: "Standard_D2v2", - AcceleratedNetworking: nil, - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - gomock.InOrder( - m.Get(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")), - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ - Location: to.StringPtr("fake-location"), - InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ - EnableAcceleratedNetworking: to.BoolPtr(true), - EnableIPForwarding: to.BoolPtr(false), - IPConfigurations: &[]network.InterfaceIPConfiguration{ - { - Name: to.StringPtr("pipConfig"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/backendAddressPools/cluster-name-outboundBackendPool")}}, - PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, - Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, - }, - }, - }, - }, - }))) - }, - }, - { - name: "control plane network interface successfully created", - expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - PublicLBName: "my-public-lb", - PublicLBAddressPoolName: "my-public-lb-backendPool", - PublicLBNATRuleName: "azure-test1", - InternalLBName: "my-internal-lb", - InternalLBAddressPoolName: "my-internal-lb-backendPool", - VMSize: "Standard_D2v2", - AcceleratedNetworking: nil, - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - m.Get(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ - Location: to.StringPtr("fake-location"), - InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ - EnableAcceleratedNetworking: to.BoolPtr(true), - EnableIPForwarding: to.BoolPtr(false), - IPConfigurations: &[]network.InterfaceIPConfiguration{ - { - Name: to.StringPtr("pipConfig"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, - PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, - LoadBalancerInboundNatRules: &[]network.InboundNatRule{{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/inboundNatRules/azure-test1")}}, - LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{ - {ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/backendAddressPools/my-public-lb-backendPool")}, - {ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-internal-lb/backendAddressPools/my-internal-lb-backendPool")}}, - }, - }, - }, - }, - })) - }, - }, - { - name: "network interface with Public IP successfully created", + name: "successfully create a network interface", expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-public-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - PublicIPName: "my-public-ip", - VMSize: "Standard_D2v2", - AcceleratedNetworking: nil, - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - m.Get(gomockinternal.AContext(), "my-rg", "my-public-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-public-net-interface", gomock.AssignableToTypeOf(network.Interface{})) + expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { + s.NICSpecs().Return([]azure.ResourceSpecGetter{&fakeNICSpec1}) + r.CreateResource(gomockinternal.AContext(), &fakeNICSpec1, serviceName).Return(nil, nil) + s.UpdatePutStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, nil) }, }, { - name: "network interface with accelerated networking successfully created", + name: "successfully create multiple network interfaces", expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - PublicLBName: "my-public-lb", - VMSize: "Standard_D2v2", - AcceleratedNetworking: nil, - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - m.Get(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ - Location: to.StringPtr("fake-location"), - InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ - EnableAcceleratedNetworking: to.BoolPtr(true), - EnableIPForwarding: to.BoolPtr(false), - IPConfigurations: &[]network.InterfaceIPConfiguration{ - { - Name: to.StringPtr("pipConfig"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, - PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, - LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{}, - }, - }, - }, - }, - }), - ) + expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { + s.NICSpecs().Return([]azure.ResourceSpecGetter{&fakeNICSpec1, &fakeNICSpec2}) + r.CreateResource(gomockinternal.AContext(), &fakeNICSpec1, serviceName).Return(nil, nil) + r.CreateResource(gomockinternal.AContext(), &fakeNICSpec2, serviceName).Return(nil, nil) + s.UpdatePutStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, nil) }, }, { - name: "network interface without accelerated networking successfully created", - expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - VNetResourceGroup: "my-rg", - PublicLBName: "my-public-lb", - VMSize: "Standard_D2v2", - AcceleratedNetworking: to.BoolPtr(false), - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - m.Get(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ - Location: to.StringPtr("fake-location"), - InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ - EnableAcceleratedNetworking: to.BoolPtr(false), - EnableIPForwarding: to.BoolPtr(false), - IPConfigurations: &[]network.InterfaceIPConfiguration{ - { - Name: to.StringPtr("pipConfig"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, - PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, - LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{}, - }, - }, - }, - }, - })) - }, - }, - { - name: "network interface with ipv6 created successfully", - expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - MachineName: "azure-test1", - SubnetName: "my-subnet", - VNetName: "my-vnet", - IPv6Enabled: true, - VNetResourceGroup: "my-rg", - PublicLBName: "my-public-lb", - VMSize: "Standard_D2v2", - AcceleratedNetworking: nil, - EnableIPForwarding: true, - }, - }) - s.SubscriptionID().AnyTimes().Return("123") - s.ResourceGroup().AnyTimes().Return("my-rg") - s.Location().AnyTimes().Return("fake-location") - gomock.InOrder( - m.Get(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(network.Interface{}, autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")), - m.CreateOrUpdate(gomockinternal.AContext(), "my-rg", "my-net-interface", gomockinternal.DiffEq(network.Interface{ - Location: to.StringPtr("fake-location"), - InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ - EnableAcceleratedNetworking: to.BoolPtr(true), - EnableIPForwarding: to.BoolPtr(true), - IPConfigurations: &[]network.InterfaceIPConfiguration{ - { - Name: to.StringPtr("pipConfig"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, - PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, - LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{}, - }, - }, - { - Name: to.StringPtr("ipConfigv6"), - InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ - Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, - Primary: to.BoolPtr(false), - PrivateIPAddressVersion: "IPv6", - }, - }, - }, - }, - })), - ) + name: "network interface create fails", + expectedError: internalError.Error(), + expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { + s.NICSpecs().Return([]azure.ResourceSpecGetter{&fakeNICSpec1, &fakeNICSpec2}) + r.CreateResource(gomockinternal.AContext(), &fakeNICSpec1, serviceName).Return(nil, internalError) + r.CreateResource(gomockinternal.AContext(), &fakeNICSpec2, serviceName).Return(nil, nil) + s.UpdatePutStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, internalError) }, }, } @@ -396,34 +106,13 @@ func TestReconcileNetworkInterface(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() scopeMock := mock_networkinterfaces.NewMockNICScope(mockCtrl) - clientMock := mock_networkinterfaces.NewMockClient(mockCtrl) + asyncMock := mock_async.NewMockReconciler(mockCtrl) - tc.expect(scopeMock.EXPECT(), clientMock.EXPECT()) + tc.expect(scopeMock.EXPECT(), asyncMock.EXPECT()) s := &Service{ - Scope: scopeMock, - Client: clientMock, - resourceSKUCache: resourceskus.NewStaticCache([]compute.ResourceSku{ - { - Name: to.StringPtr("Standard_D2v2"), - Kind: to.StringPtr(string(resourceskus.VirtualMachines)), - Locations: &[]string{ - "fake-location", - }, - LocationInfo: &[]compute.ResourceSkuLocationInfo{ - { - Location: to.StringPtr("fake-location"), - Zones: &[]string{"1"}, - }, - }, - Capabilities: &[]compute.ResourceSkuCapabilities{ - { - Name: to.StringPtr(resourceskus.AcceleratedNetworking), - Value: to.StringPtr(string(resourceskus.CapabilitySupported)), - }, - }, - }, - }, ""), + Scope: scopeMock, + Reconciler: asyncMock, } err := s.Reconcile(context.TODO()) @@ -443,53 +132,35 @@ func TestDeleteNetworkInterface(t *testing.T) { testcases := []struct { name string expectedError string - expect func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) + expect func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) }{ { name: "successfully delete an existing network interface", expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - PublicLBName: "my-public-lb", - MachineName: "azure-test1", - }, - }) - s.ResourceGroup().AnyTimes().Return("my-rg") - m.Delete(gomockinternal.AContext(), "my-rg", "my-net-interface") + expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { + s.NICSpecs().Return([]azure.ResourceSpecGetter{&fakeNICSpec1}) + r.DeleteResource(gomockinternal.AContext(), &fakeNICSpec1, serviceName).Return(nil) + s.UpdateDeleteStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, nil) }, }, { - name: "network interface already deleted", + name: "successfully delete multiple existing network interfaces", expectedError: "", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - PublicLBName: "my-public-lb", - MachineName: "azure-test1", - }, - }) - s.ResourceGroup().AnyTimes().Return("my-rg") - m.Delete(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 404}, "Not found")) + expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { + s.NICSpecs().Return([]azure.ResourceSpecGetter{&fakeNICSpec1, &fakeNICSpec2}) + r.DeleteResource(gomockinternal.AContext(), &fakeNICSpec1, serviceName).Return(nil) + r.DeleteResource(gomockinternal.AContext(), &fakeNICSpec2, serviceName).Return(nil) + s.UpdateDeleteStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, nil) }, }, { name: "network interface deletion fails", - expectedError: "failed to delete network interface my-net-interface in resource group my-rg: #: Internal Server Error: StatusCode=500", - expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, m *mock_networkinterfaces.MockClientMockRecorder) { - s.NICSpecs().Return([]azure.NICSpec{ - { - Name: "my-net-interface", - PublicLBName: "my-public-lb", - MachineName: "azure-test1", - }, - }) - s.ResourceGroup().AnyTimes().Return("my-rg") - m.Delete(gomockinternal.AContext(), "my-rg", "my-net-interface"). - Return(autorest.NewErrorWithResponse("", "", &http.Response{StatusCode: 500}, "Internal Server Error")) + expectedError: internalError.Error(), + expect: func(s *mock_networkinterfaces.MockNICScopeMockRecorder, r *mock_async.MockReconcilerMockRecorder) { + s.NICSpecs().Return([]azure.ResourceSpecGetter{&fakeNICSpec1, &fakeNICSpec2}) + r.DeleteResource(gomockinternal.AContext(), &fakeNICSpec1, serviceName).Return(nil) + r.DeleteResource(gomockinternal.AContext(), &fakeNICSpec2, serviceName).Return(internalError) + s.UpdateDeleteStatus(infrav1.NetworkInterfaceReadyCondition, serviceName, internalError) }, }, } @@ -502,13 +173,13 @@ func TestDeleteNetworkInterface(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() scopeMock := mock_networkinterfaces.NewMockNICScope(mockCtrl) - clientMock := mock_networkinterfaces.NewMockClient(mockCtrl) + asyncMock := mock_async.NewMockReconciler(mockCtrl) - tc.expect(scopeMock.EXPECT(), clientMock.EXPECT()) + tc.expect(scopeMock.EXPECT(), asyncMock.EXPECT()) s := &Service{ - Scope: scopeMock, - Client: clientMock, + Scope: scopeMock, + Reconciler: asyncMock, } err := s.Delete(context.TODO()) diff --git a/azure/services/networkinterfaces/spec.go b/azure/services/networkinterfaces/spec.go new file mode 100644 index 00000000000..c926f35b3ea --- /dev/null +++ b/azure/services/networkinterfaces/spec.go @@ -0,0 +1,156 @@ +/* +Copyright 2021 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 networkinterfaces + +import ( + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network" + "github.com/Azure/go-autorest/autorest/to" + "github.com/pkg/errors" + "sigs.k8s.io/cluster-api-provider-azure/azure" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus" +) + +// NICSpec defines the specification for a Network Interface. +type NICSpec struct { + Name string + ResourceGroup string + Location string + SubscriptionID string + MachineName string + SubnetName string + VNetName string + VNetResourceGroup string + StaticIPAddress string + PublicLBName string + PublicLBAddressPoolName string + PublicLBNATRuleName string + InternalLBName string + InternalLBAddressPoolName string + PublicIPName string + AcceleratedNetworking *bool + IPv6Enabled bool + EnableIPForwarding bool + SKU *resourceskus.SKU +} + +// ResourceName returns the name of the network interface. +func (s *NICSpec) ResourceName() string { + return s.Name +} + +// ResourceGroupName returns the name of the resource group. +func (s *NICSpec) ResourceGroupName() string { + return s.ResourceGroup +} + +// OwnerResourceName is a no-op for network interfaces. +func (s *NICSpec) OwnerResourceName() string { + return "" +} + +// Parameters returns the parameters for the network interface. +func (s *NICSpec) Parameters(existing interface{}) (parameters interface{}, err error) { + if existing != nil { + if _, ok := existing.(network.Interface); !ok { + return nil, errors.Errorf("%T is not a network.Interface", existing) + } + // network interface already exists + return nil, nil + } + + nicConfig := &network.InterfaceIPConfigurationPropertiesFormat{} + + subnet := &network.Subnet{ + ID: to.StringPtr(azure.SubnetID(s.SubscriptionID, s.VNetResourceGroup, s.VNetName, s.SubnetName)), + } + nicConfig.Subnet = subnet + + nicConfig.PrivateIPAllocationMethod = network.IPAllocationMethodDynamic + if s.StaticIPAddress != "" { + nicConfig.PrivateIPAllocationMethod = network.IPAllocationMethodStatic + nicConfig.PrivateIPAddress = to.StringPtr(s.StaticIPAddress) + } + + backendAddressPools := []network.BackendAddressPool{} + if s.PublicLBName != "" { + if s.PublicLBAddressPoolName != "" { + backendAddressPools = append(backendAddressPools, + network.BackendAddressPool{ + ID: to.StringPtr(azure.AddressPoolID(s.SubscriptionID, s.ResourceGroup, s.PublicLBName, s.PublicLBAddressPoolName)), + }) + } + if s.PublicLBNATRuleName != "" { + nicConfig.LoadBalancerInboundNatRules = &[]network.InboundNatRule{ + { + ID: to.StringPtr(azure.NATRuleID(s.SubscriptionID, s.ResourceGroup, s.PublicLBName, s.PublicLBNATRuleName)), + }, + } + } + } + if s.InternalLBName != "" && s.InternalLBAddressPoolName != "" { + backendAddressPools = append(backendAddressPools, + network.BackendAddressPool{ + ID: to.StringPtr(azure.AddressPoolID(s.SubscriptionID, s.ResourceGroup, s.InternalLBName, s.InternalLBAddressPoolName)), + }) + } + nicConfig.LoadBalancerBackendAddressPools = &backendAddressPools + + if s.PublicIPName != "" { + nicConfig.PublicIPAddress = &network.PublicIPAddress{ + ID: to.StringPtr(azure.PublicIPID(s.SubscriptionID, s.ResourceGroup, s.PublicIPName)), + } + } + + if s.AcceleratedNetworking == nil { + // set accelerated networking to the capability of the VMSize + if s.SKU == nil { + return nil, errors.New("unable to get required network interface SKU from machine cache") + } + + accelNet := s.SKU.HasCapability(resourceskus.AcceleratedNetworking) + s.AcceleratedNetworking = &accelNet + } + + ipConfigurations := []network.InterfaceIPConfiguration{ + { + Name: to.StringPtr("pipConfig"), + InterfaceIPConfigurationPropertiesFormat: nicConfig, + }, + } + + if s.IPv6Enabled { + ipv6Config := network.InterfaceIPConfiguration{ + Name: to.StringPtr("ipConfigv6"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + PrivateIPAddressVersion: "IPv6", + Primary: to.BoolPtr(false), + Subnet: &network.Subnet{ID: subnet.ID}, + }, + } + + ipConfigurations = append(ipConfigurations, ipv6Config) + } + + return network.Interface{ + Location: to.StringPtr(s.Location), + InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ + EnableAcceleratedNetworking: s.AcceleratedNetworking, + IPConfigurations: &ipConfigurations, + EnableIPForwarding: to.BoolPtr(s.EnableIPForwarding), + }, + }, nil +} diff --git a/azure/services/networkinterfaces/spec_test.go b/azure/services/networkinterfaces/spec_test.go new file mode 100644 index 00000000000..c39b45fd219 --- /dev/null +++ b/azure/services/networkinterfaces/spec_test.go @@ -0,0 +1,356 @@ +/* +Copyright 2021 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 networkinterfaces + +import ( + "testing" + + "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network" + "github.com/Azure/go-autorest/autorest/to" + . "github.com/onsi/gomega" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/resourceskus" +) + +var ( + fakeMissingSKUNICSpec = NICSpec{ + Name: "my-net-interface", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + PublicLBName: "my-public-lb", + AcceleratedNetworking: nil, + } + fakeSku = resourceskus.SKU{ + Name: to.StringPtr("Standard_D2v2"), + Kind: to.StringPtr(string(resourceskus.VirtualMachines)), + Locations: &[]string{ + "fake-location", + }, + LocationInfo: &[]compute.ResourceSkuLocationInfo{ + { + Location: to.StringPtr("fake-location"), + Zones: &[]string{"1"}, + }, + }, + Capabilities: &[]compute.ResourceSkuCapabilities{ + { + Name: to.StringPtr(resourceskus.AcceleratedNetworking), + Value: to.StringPtr(string(resourceskus.CapabilitySupported)), + }, + }, + } + fakeStaticPrivateIPNICSpec = NICSpec{ + Name: "my-net-interface", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + PublicLBName: "my-public-lb", + PublicLBAddressPoolName: "cluster-name-outboundBackendPool", + StaticIPAddress: "fake.static.ip", + AcceleratedNetworking: nil, + SKU: &fakeSku, + } + + fakeDynamicPrivateIPNICSpec = NICSpec{ + Name: "my-net-interface", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + PublicLBName: "my-public-lb", + PublicLBAddressPoolName: "cluster-name-outboundBackendPool", + AcceleratedNetworking: nil, + SKU: &fakeSku, + } + + fakeControlPlaneNICSpec = NICSpec{ + Name: "my-net-interface", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + PublicLBName: "my-public-lb", + PublicLBAddressPoolName: "my-public-lb-backendPool", + PublicLBNATRuleName: "azure-test1", + InternalLBName: "my-internal-lb", + InternalLBAddressPoolName: "my-internal-lb-backendPool", + AcceleratedNetworking: nil, + SKU: &fakeSku, + } + + fakeAcceleratedNetworkingNICSpec = NICSpec{ + Name: "my-net-interface", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + PublicLBName: "my-public-lb", + AcceleratedNetworking: nil, + SKU: &fakeSku, + } + + fakeNonAcceleratedNetworkingNICSpec = NICSpec{ + Name: "my-net-interface", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + VNetResourceGroup: "my-rg", + PublicLBName: "my-public-lb", + AcceleratedNetworking: to.BoolPtr(false), + } + + fakeIpv6NICSpec = NICSpec{ + Name: "my-net-interface", + ResourceGroup: "my-rg", + Location: "fake-location", + SubscriptionID: "123", + MachineName: "azure-test1", + SubnetName: "my-subnet", + VNetName: "my-vnet", + IPv6Enabled: true, + VNetResourceGroup: "my-rg", + PublicLBName: "my-public-lb", + AcceleratedNetworking: nil, + SKU: &fakeSku, + EnableIPForwarding: true, + } +) + +func TestParameters(t *testing.T) { + testcases := []struct { + name string + spec *NICSpec + existing interface{} + expect func(g *WithT, result interface{}) + expectedError string + }{ + { + name: "error when accelerted networking is nil and no SKU is present", + spec: &fakeMissingSKUNICSpec, + existing: nil, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeNil()) + }, + expectedError: "unable to get required network interface SKU from machine cache", + }, + { + name: "get parameters for network interface with static private IP", + spec: &fakeStaticPrivateIPNICSpec, + existing: nil, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeAssignableToTypeOf(network.Interface{})) + g.Expect(result.(network.Interface)).To(Equal(network.Interface{ + Location: to.StringPtr("fake-location"), + InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ + EnableAcceleratedNetworking: to.BoolPtr(true), + EnableIPForwarding: to.BoolPtr(false), + IPConfigurations: &[]network.InterfaceIPConfiguration{ + { + Name: to.StringPtr("pipConfig"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/backendAddressPools/cluster-name-outboundBackendPool")}}, + PrivateIPAllocationMethod: network.IPAllocationMethodStatic, + PrivateIPAddress: to.StringPtr("fake.static.ip"), + Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, + }, + }, + }, + }, + })) + }, + expectedError: "", + }, + { + name: "get parameters for network interface with dynamic private IP", + spec: &fakeDynamicPrivateIPNICSpec, + existing: nil, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeAssignableToTypeOf(network.Interface{})) + g.Expect(result.(network.Interface)).To(Equal(network.Interface{ + Location: to.StringPtr("fake-location"), + InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ + EnableAcceleratedNetworking: to.BoolPtr(true), + EnableIPForwarding: to.BoolPtr(false), + IPConfigurations: &[]network.InterfaceIPConfiguration{ + { + Name: to.StringPtr("pipConfig"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/backendAddressPools/cluster-name-outboundBackendPool")}}, + PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, + Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, + }, + }, + }, + }, + })) + }, + expectedError: "", + }, + { + name: "get parameters for control plane network interface", + spec: &fakeControlPlaneNICSpec, + existing: nil, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeAssignableToTypeOf(network.Interface{})) + g.Expect(result.(network.Interface)).To(Equal(network.Interface{ + Location: to.StringPtr("fake-location"), + InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ + EnableAcceleratedNetworking: to.BoolPtr(true), + EnableIPForwarding: to.BoolPtr(false), + IPConfigurations: &[]network.InterfaceIPConfiguration{ + { + Name: to.StringPtr("pipConfig"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, + PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, + LoadBalancerInboundNatRules: &[]network.InboundNatRule{{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/inboundNatRules/azure-test1")}}, + LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{ + {ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-public-lb/backendAddressPools/my-public-lb-backendPool")}, + {ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/loadBalancers/my-internal-lb/backendAddressPools/my-internal-lb-backendPool")}}, + }, + }, + }, + }, + })) + }, + expectedError: "", + }, + { + name: "get parameters for network interface with accelerated networking", + spec: &fakeAcceleratedNetworkingNICSpec, + existing: nil, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeAssignableToTypeOf(network.Interface{})) + g.Expect(result.(network.Interface)).To(Equal(network.Interface{ + Location: to.StringPtr("fake-location"), + InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ + EnableAcceleratedNetworking: to.BoolPtr(true), + EnableIPForwarding: to.BoolPtr(false), + IPConfigurations: &[]network.InterfaceIPConfiguration{ + { + Name: to.StringPtr("pipConfig"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, + PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, + LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{}, + }, + }, + }, + }, + })) + }, + expectedError: "", + }, + { + name: "get parameters for network interface without accelerated networking", + spec: &fakeNonAcceleratedNetworkingNICSpec, + existing: nil, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeAssignableToTypeOf(network.Interface{})) + g.Expect(result.(network.Interface)).To(Equal(network.Interface{ + Location: to.StringPtr("fake-location"), + InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ + EnableAcceleratedNetworking: to.BoolPtr(false), + EnableIPForwarding: to.BoolPtr(false), + IPConfigurations: &[]network.InterfaceIPConfiguration{ + { + Name: to.StringPtr("pipConfig"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, + PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, + LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{}, + }, + }, + }, + }, + })) + }, + expectedError: "", + }, + { + name: "get parameters for network interface ipv6", + spec: &fakeIpv6NICSpec, + existing: nil, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeAssignableToTypeOf(network.Interface{})) + g.Expect(result.(network.Interface)).To(Equal(network.Interface{ + Location: to.StringPtr("fake-location"), + InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ + EnableAcceleratedNetworking: to.BoolPtr(true), + EnableIPForwarding: to.BoolPtr(true), + IPConfigurations: &[]network.InterfaceIPConfiguration{ + { + Name: to.StringPtr("pipConfig"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, + PrivateIPAllocationMethod: network.IPAllocationMethodDynamic, + LoadBalancerBackendAddressPools: &[]network.BackendAddressPool{}, + }, + }, + { + Name: to.StringPtr("ipConfigv6"), + InterfaceIPConfigurationPropertiesFormat: &network.InterfaceIPConfigurationPropertiesFormat{ + Subnet: &network.Subnet{ID: to.StringPtr("/subscriptions/123/resourceGroups/my-rg/providers/Microsoft.Network/virtualNetworks/my-vnet/subnets/my-subnet")}, + Primary: to.BoolPtr(false), + PrivateIPAddressVersion: "IPv6", + }, + }, + }, + }, + })) + }, + expectedError: "", + }, + } + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + g := NewWithT(t) + t.Parallel() + + result, err := tc.spec.Parameters(tc.existing) + if tc.expectedError != "" { + g.Expect(err).To(HaveOccurred()) + g.Expect(err).To(MatchError(tc.expectedError)) + } else { + g.Expect(err).NotTo(HaveOccurred()) + } + tc.expect(g, result) + }) + } +} diff --git a/azure/services/virtualmachines/client.go b/azure/services/virtualmachines/client.go index a7ba8c7f8ec..ccf98eb27a2 100644 --- a/azure/services/virtualmachines/client.go +++ b/azure/services/virtualmachines/client.go @@ -32,8 +32,8 @@ import ( "sigs.k8s.io/cluster-api-provider-azure/util/tele" ) -// Client wraps go-sdk. type ( + // Client wraps go-sdk. Client interface { Get(context.Context, azure.ResourceSpecGetter) (interface{}, error) CreateOrUpdateAsync(ctx context.Context, spec azure.ResourceSpecGetter, parameters interface{}) (result interface{}, future azureautorest.FutureAPI, err error) diff --git a/azure/services/virtualmachines/virtualmachines.go b/azure/services/virtualmachines/virtualmachines.go index 906bcb01b3f..abe96b9f3c5 100644 --- a/azure/services/virtualmachines/virtualmachines.go +++ b/azure/services/virtualmachines/virtualmachines.go @@ -21,6 +21,7 @@ import ( "strings" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-04-01/compute" + "github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network" "github.com/Azure/go-autorest/autorest/to" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" @@ -145,11 +146,19 @@ func (s *Service) getAddresses(ctx context.Context, vm compute.VirtualMachine, r nicName := getResourceNameByID(to.String(nicRef.ID)) // Fetch nic and append its addresses - nic, err := s.interfacesClient.Get(ctx, rgName, nicName) + existingNic, err := s.interfacesClient.Get(ctx, &networkinterfaces.NICSpec{ + Name: nicName, + ResourceGroup: rgName, + }) if err != nil { return addresses, err } + nic, ok := existingNic.(network.Interface) + if !ok { + return nil, errors.Errorf("%T is not a network.Interface", existingNic) + } + if nic.IPConfigurations == nil { continue } diff --git a/azure/services/virtualmachines/virtualmachines_test.go b/azure/services/virtualmachines/virtualmachines_test.go index 998b8dc1a70..8a63b629b24 100644 --- a/azure/services/virtualmachines/virtualmachines_test.go +++ b/azure/services/virtualmachines/virtualmachines_test.go @@ -31,6 +31,7 @@ import ( infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" "sigs.k8s.io/cluster-api-provider-azure/azure/services/async/mock_async" "sigs.k8s.io/cluster-api-provider-azure/azure/services/availabilitysets/mock_availabilitysets" + "sigs.k8s.io/cluster-api-provider-azure/azure/services/networkinterfaces" "sigs.k8s.io/cluster-api-provider-azure/azure/services/networkinterfaces/mock_networkinterfaces" "sigs.k8s.io/cluster-api-provider-azure/azure/services/publicips/mock_publicips" "sigs.k8s.io/cluster-api-provider-azure/azure/services/virtualmachines/mock_virtualmachines" @@ -66,6 +67,10 @@ var ( }, }, } + fakeNetworkInterfaceGetterSpec = networkinterfaces.NICSpec{ + Name: "nic-1", + ResourceGroup: "test-group", + } fakeNetworkInterface = network.Interface{ InterfacePropertiesFormat: &network.InterfacePropertiesFormat{ IPConfigurations: &[]network.InterfaceIPConfiguration{ @@ -114,7 +119,7 @@ func TestReconcileVM(t *testing.T) { s.UpdatePutStatus(infrav1.DisksReadyCondition, serviceName, nil) s.SetProviderID("azure://test-vm-id") s.SetAnnotation("cluster-api-provider-azure", "true") - mnic.Get(gomockinternal.AContext(), "test-group", "nic-1").Return(fakeNetworkInterface, nil) + mnic.Get(gomockinternal.AContext(), &fakeNetworkInterfaceGetterSpec).Return(fakeNetworkInterface, nil) mpip.Get(gomockinternal.AContext(), "test-group", "pip-1").Return(fakePublicIPs, nil) s.SetAddresses(fakeNodeAddresses) s.SetVMState(infrav1.Succeeded) @@ -140,7 +145,7 @@ func TestReconcileVM(t *testing.T) { s.UpdatePutStatus(infrav1.DisksReadyCondition, serviceName, nil) s.SetProviderID("azure://test-vm-id") s.SetAnnotation("cluster-api-provider-azure", "true") - mnic.Get(gomockinternal.AContext(), "test-group", "nic-1").Return(network.Interface{}, internalError) + mnic.Get(gomockinternal.AContext(), &fakeNetworkInterfaceGetterSpec).Return(network.Interface{}, internalError) }, }, { @@ -153,7 +158,7 @@ func TestReconcileVM(t *testing.T) { s.UpdatePutStatus(infrav1.DisksReadyCondition, serviceName, nil) s.SetProviderID("azure://test-vm-id") s.SetAnnotation("cluster-api-provider-azure", "true") - mnic.Get(gomockinternal.AContext(), "test-group", "nic-1").Return(fakeNetworkInterface, nil) + mnic.Get(gomockinternal.AContext(), &fakeNetworkInterfaceGetterSpec).Return(fakeNetworkInterface, nil) mpip.Get(gomockinternal.AContext(), "test-group", "pip-1").Return(network.PublicIPAddress{}, internalError) }, }, diff --git a/azure/types.go b/azure/types.go index 09a2b897530..fd547fb7f12 100644 --- a/azure/types.go +++ b/azure/types.go @@ -31,26 +31,6 @@ type PublicIPSpec struct { IsIPv6 bool } -// NICSpec defines the specification for a Network Interface. -type NICSpec struct { - Name string - MachineName string - SubnetName string - VNetName string - VNetResourceGroup string - StaticIPAddress string - PublicLBName string - PublicLBAddressPoolName string - PublicLBNATRuleName string - InternalLBName string - InternalLBAddressPoolName string - PublicIPName string - VMSize string - AcceleratedNetworking *bool - IPv6Enabled bool - EnableIPForwarding bool -} - // SubnetSpec defines the specification for a Subnet. type SubnetSpec struct { Name string