Skip to content

Commit a46fc1f

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. (cherry picked from commit f76ad07) OCPBUGS-55372: Fix regression on ASH with armcompute/v5 The change "Update virtualmachines service to armcompute/v5 SDK" did not update the StackHub implementation of the virtualmachines service, which caused a failure in the machine actuator due to the difference in type returned by Get(). We fix this by moving to a common implementation for both StackHub and public clouds. Having a common implementation in virtualmachines also requires a change to the networkinterfaces Service. The networkinterfaces service Get() method returns different types for StackHub and public clouds, which were previously cast to the anticipated types by different implementations of the virtualmachines service. As we don't require the full type, we add a new type safe method which returns only the ID, meaning a common implementation is now safe. (cherry picked from commit afd0f3f)
1 parent 249e31e commit a46fc1f

20 files changed

+547
-645
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

go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
22
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
3+
github.com/Azure/azure-sdk-for-go-extensions v0.1.8 h1:x8Vu78C4r8mh6V2yQKQRSWLU+EYBVwFsf3XECddyb6s=
4+
github.com/Azure/azure-sdk-for-go-extensions v0.1.8/go.mod h1:4su5NjJwhqFH2B/5zJSKOz7hazfr2y38Iu6W4ZK0HYA=
35
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 h1:1nGuui+4POelzDwI7RG56yfQJHCnKvwfMoU7VsEp+Zg=
46
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI=
57
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
68
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
79
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o=
810
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc=
11+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 h1:LkHbJbgF3YyvC53aqYGR+wWQDn2Rdp9AQdGndf9QvY4=
12+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0/go.mod h1:QyiQdW4f4/BIfB8ZutZ2s+28RAgfa/pT+zS++ZHyM1I=
13+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
14+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0/go.mod h1:LRr2FzBTQlONPPa5HREE5+RjSCTXl7BwOvYOaWTqCaI=
15+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1 h1:7CBQ+Ei8SP2c6ydQTGCCrS35bDxgTMfoP2miAwK++OU=
16+
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.1.1/go.mod h1:c/wcGeGx5FUPbM/JltUYHZcKmigwyVLJlDq+4HdtXaw=
917
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
1018
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
1119
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=

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)