Skip to content

Commit f76ad07

Browse files
committed
Update virtualmachines service to armcompute/v5 SDK
This replaces use of the legacy /services SDK for compute only in the virtualmachines service, with a minimal set of supporting changes. This allows using Azure APIs beyond those available via the legacy SDK.
1 parent dd3ca57 commit f76ad07

File tree

13 files changed

+421
-387
lines changed

13 files changed

+421
-387
lines changed

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ go 1.23.0
44

55
require (
66
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
7+
github.com/Azure/azure-sdk-for-go-extensions v0.1.8
78
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0
89
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0
10+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0
911
github.com/Azure/go-autorest/autorest v0.11.29
1012
github.com/Azure/go-autorest/autorest/to v0.4.0
1113
github.com/ghodss/yaml v1.0.0

pkg/cloud/azure/actuators/clients.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,47 @@ limitations under the License.
1717
package actuators
1818

1919
import (
20+
"net/http"
21+
22+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
23+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
24+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
25+
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
2026
"github.com/Azure/go-autorest/autorest"
27+
28+
"github.com/openshift/machine-api-provider-azure/pkg/cloud/azure"
2129
)
2230

2331
// AzureClients contains all the Azure clients used by the scopes.
2432
type AzureClients struct {
2533
SubscriptionID string
2634
Authorizer autorest.Authorizer
2735
ResourceManagerEndpoint string
36+
37+
// For SDK v2
38+
Token azcore.TokenCredential
39+
Cloud cloud.Configuration
40+
}
41+
42+
// ARMClientOptions returns default ARM client options for CAPZ SDK v2 requests.
43+
func (c *AzureClients) ARMClientOptions() *arm.ClientOptions {
44+
opts := &arm.ClientOptions{}
45+
opts.Cloud = c.Cloud
46+
opts.PerCallPolicies = []policy.Policy{
47+
userAgentPolicy{},
48+
}
49+
opts.Retry.MaxRetries = -1 // Less than zero means one try and no retries.
50+
51+
return opts
52+
}
53+
54+
// userAgentPolicy extends the "User-Agent" header on requests.
55+
// It implements the policy.Policy interface.
56+
type userAgentPolicy struct{}
57+
58+
// Do extends the "User-Agent" header of a request by appending CAPZ's user agent.
59+
func (p userAgentPolicy) Do(req *policy.Request) (*http.Response, error) {
60+
// FIXME: Ought to include a version after our UA string if we have one
61+
req.Raw().Header.Set("User-Agent", req.Raw().UserAgent()+" "+azure.UserAgent)
62+
return req.Next()
2863
}

pkg/cloud/azure/actuators/machine/actuator.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"fmt"
2323
"time"
2424

25-
"github.com/Azure/go-autorest/autorest"
25+
azureerrors "github.com/Azure/azure-sdk-for-go-extensions/pkg/errors"
2626
machinev1 "github.com/openshift/api/machine/v1beta1"
2727
machineapierrors "github.com/openshift/machine-api-operator/pkg/controller/machine"
2828
"github.com/openshift/machine-api-provider-azure/pkg/cloud/azure"
@@ -106,15 +106,13 @@ func (a *Actuator) Create(ctx context.Context, machine *machinev1.Machine) error
106106
klog.Errorf("Error storing machine info: %v", err)
107107
}
108108

109-
var detailedError autorest.DetailedError
110-
if errors.As(err, &detailedError) {
111-
statusCode, ok := detailedError.StatusCode.(int)
109+
if azErr := azureerrors.IsResponseError(err); azErr != nil {
112110
// Any 4xx error that isn't invalid credentials should be a terminal failure.
113111
// Invalid Credentials implies that the credentials expired between the scope creation and API calls,
114112
// this may happen when CCO is refreshing credentials simultaneously.
115113
// In this case we should retry as the credentials should be updated in the secret.
116-
if ok && statusCode >= 400 && statusCode < 500 && !azure.InvalidCredentials(err) {
117-
return a.handleMachineError(machine, machineapierrors.InvalidMachineConfiguration("failed to reconcile machine %q: %v", machine.Name, detailedError), createEventAction)
114+
if azErr.StatusCode >= 400 && azErr.StatusCode < 500 && !azure.InvalidCredentials(err) {
115+
return a.handleMachineError(machine, machineapierrors.InvalidMachineConfiguration("failed to reconcile machine %q: %v", machine.Name, err), createEventAction)
118116
}
119117
}
120118

pkg/cloud/azure/actuators/machine/actuator_test.go

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ import (
2525
"strings"
2626
"testing"
2727

28-
"github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2021-11-01/compute"
28+
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
29+
"github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5"
2930
"github.com/Azure/azure-sdk-for-go/services/network/mgmt/2021-02-01/network"
30-
"github.com/Azure/go-autorest/autorest"
3131
"github.com/Azure/go-autorest/autorest/to"
3232
"github.com/ghodss/yaml"
3333
"github.com/golang/mock/gomock"
@@ -181,21 +181,22 @@ type FakeVMService struct {
181181
// Get returns fake success.
182182
func (s *FakeVMService) Get(ctx context.Context, spec azure.Spec) (interface{}, error) {
183183
s.GetCallCount++
184-
return compute.VirtualMachine{
184+
vm := armcompute.VirtualMachine{
185185
ID: to.StringPtr(s.ID),
186186
Name: to.StringPtr(s.Name),
187-
VirtualMachineProperties: &compute.VirtualMachineProperties{
187+
Properties: &armcompute.VirtualMachineProperties{
188188
ProvisioningState: to.StringPtr(s.ProvisioningState),
189-
NetworkProfile: &compute.NetworkProfile{
190-
NetworkInterfaces: &[]compute.NetworkInterfaceReference{
189+
NetworkProfile: &armcompute.NetworkProfile{
190+
NetworkInterfaces: []*armcompute.NetworkInterfaceReference{
191191
{
192-
ID: to.StringPtr("machine-test-nic"),
193-
NetworkInterfaceReferenceProperties: &compute.NetworkInterfaceReferenceProperties{},
192+
ID: to.StringPtr("machine-test-nic"),
193+
Properties: &armcompute.NetworkInterfaceReferenceProperties{},
194194
},
195195
},
196196
},
197197
},
198-
}, nil
198+
}
199+
return armcompute.VirtualMachinesClientGetResponse{VirtualMachine: vm}, nil
199200
}
200201

201202
// CreateOrUpdate returns fake success.
@@ -393,9 +394,10 @@ func (s *FakeVMCheckZonesService) Get(ctx context.Context, spec azure.Spec) (int
393394
return nil, errors.New("vm not found")
394395
}
395396

396-
return &compute.VirtualMachine{
397-
VirtualMachineProperties: &compute.VirtualMachineProperties{},
398-
}, nil
397+
vm := armcompute.VirtualMachine{
398+
Properties: &armcompute.VirtualMachineProperties{},
399+
}
400+
return armcompute.VirtualMachinesClientGetResponse{VirtualMachine: vm}, nil
399401
}
400402

401403
// CreateOrUpdate returns fake success.
@@ -731,19 +733,20 @@ func TestMachineEvents(t *testing.T) {
731733
})
732734

733735
networkSvc.EXPECT().CreateOrUpdate(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
734-
vmSvc.EXPECT().Get(gomock.Any(), gomock.Any()).Return(compute.VirtualMachine{
736+
vm := armcompute.VirtualMachine{
735737
ID: ptr.To[string]("vm-id"),
736738
Name: ptr.To[string]("vm-name"),
737-
VirtualMachineProperties: &compute.VirtualMachineProperties{
739+
Properties: &armcompute.VirtualMachineProperties{
738740
ProvisioningState: ptr.To[string]("Succeeded"),
739-
HardwareProfile: &compute.HardwareProfile{
740-
VMSize: compute.VirtualMachineSizeTypesStandardB2ms,
741+
HardwareProfile: &armcompute.HardwareProfile{
742+
VMSize: ptr.To(armcompute.VirtualMachineSizeTypesStandardB2Ms),
741743
},
742-
OsProfile: &compute.OSProfile{
744+
OSProfile: &armcompute.OSProfile{
743745
ComputerName: ptr.To[string]("vm-name"),
744746
},
745747
},
746-
}, nil).AnyTimes()
748+
}
749+
vmSvc.EXPECT().Get(gomock.Any(), gomock.Any()).Return(armcompute.VirtualMachinesClientGetResponse{VirtualMachine: vm}, nil).AnyTimes()
747750
vmSvc.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
748751
disksSvc.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
749752
networkSvc.EXPECT().Delete(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
@@ -788,25 +791,21 @@ func TestStatusCodeBasedCreationErrors(t *testing.T) {
788791
cases := []struct {
789792
name string
790793
operation func(actuator *Actuator, machine *machinev1.Machine)
791-
event string
792-
statusCode interface{}
794+
statusCode int
793795
requeable bool
794796
}{
795797
{
796798
name: "InvalidConfig",
797-
event: "Warning FailedCreate InvalidConfiguration: failed to reconcile machine \"azure-actuator-testing-machine\": compute.VirtualMachinesClient#CreateOrUpdate: MOCK: StatusCode=400",
798799
statusCode: 400,
799800
requeable: false,
800801
},
801802
{
802803
name: "CreateMachine",
803-
event: "Warning FailedCreate CreateError: failed to reconcile machine \"azure-actuator-testing-machine\"s: failed to create vm azure-actuator-testing-machine: failed to create VM: failed to create or get machine: compute.VirtualMachinesClient#CreateOrUpdate: MOCK: StatusCode=300",
804804
statusCode: 300,
805805
requeable: true,
806806
},
807807
{
808808
name: "CreateMachine",
809-
event: "Warning FailedCreate CreateError: failed to reconcile machine \"azure-actuator-testing-machine\"s: failed to create vm azure-actuator-testing-machine: failed to create VM: failed to create or get machine: compute.VirtualMachinesClient#CreateOrUpdate: MOCK: StatusCode=401",
810809
statusCode: 401,
811810
requeable: true,
812811
},
@@ -850,11 +849,13 @@ func TestStatusCodeBasedCreationErrors(t *testing.T) {
850849
})
851850

852851
networkSvc.EXPECT().CreateOrUpdate(gomock.Any(), gomock.Any()).Return(nil).Times(1)
853-
azureErr := autorest.NewError("compute.VirtualMachinesClient", "CreateOrUpdate", "MOCK")
854-
azureErr.StatusCode = tc.statusCode
852+
var azureErr error = &azcore.ResponseError{
853+
ErrorCode: fmt.Sprintf("StatusCode=%d", tc.statusCode),
854+
StatusCode: tc.statusCode,
855+
}
855856
wrapErr := fmt.Errorf("failed to create or get machine: %w", azureErr)
856857
vmSvc.EXPECT().CreateOrUpdate(gomock.Any(), gomock.Any()).Return(wrapErr).Times(1)
857-
vmSvc.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, autorest.NewError("compute.VirtualMachinesClient", "Get", "MOCK")).Times(1)
858+
vmSvc.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil, fmt.Errorf("mock compute.VirtualMachinesClient Get error")).Times(1)
858859
availabilityZonesSvc.EXPECT().Get(gomock.Any(), gomock.Any()).Return([]string{"testzone"}, nil).Times(1)
859860

860861
_, ok := machineActuator.Create(context.TODO(), machine).(*machineapierrors.RequeueAfterError)
@@ -867,11 +868,11 @@ func TestStatusCodeBasedCreationErrors(t *testing.T) {
867868

868869
select {
869870
case event := <-eventsChannel:
870-
if event != tc.event {
871-
t.Errorf("Expected %q event, got %q", tc.event, event)
871+
if !strings.Contains(event, azureErr.Error()) {
872+
t.Errorf("Expected event to contain error %q, got %q", azureErr.Error(), event)
872873
}
873874
default:
874-
t.Errorf("Expected %q event, got none", tc.event)
875+
t.Errorf("Expected an event containing error %q, got none", azureErr.Error())
875876
}
876877
})
877878
}

0 commit comments

Comments
 (0)