Skip to content
This repository has been archived by the owner on Oct 24, 2023. It is now read-only.

test: add VMSS client interfaces and mocks to make upgrade testable #590

Merged
merged 2 commits into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 6 additions & 4 deletions pkg/armhelpers/compute.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,15 @@ func (az *AzureClient) DeleteVirtualMachine(ctx context.Context, resourceGroup,
}

// ListVirtualMachineScaleSets returns (the first page of) the vmss resources in the specified resource group.
func (az *AzureClient) ListVirtualMachineScaleSets(ctx context.Context, resourceGroup string) (compute.VirtualMachineScaleSetListResultPage, error) {
return az.virtualMachineScaleSetsClient.List(ctx, resourceGroup)
func (az *AzureClient) ListVirtualMachineScaleSets(ctx context.Context, resourceGroup string) (VirtualMachineScaleSetListResultPage, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this will require a change to AKS implementation, as it consumes this client+method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It shouldn't cause a problem as the interface is 1-1 with the compute type, and all other methods currently depend on these interfaces. If we're lucky, the import will already be present.
Otherwise, good reminder to always depend on our interface types and never on the compute types...

page, err := az.virtualMachineScaleSetsClient.List(ctx, resourceGroup)
return &page, err
}

// ListVirtualMachineScaleSetVMs returns the list of VMs per VMSS
func (az *AzureClient) ListVirtualMachineScaleSetVMs(ctx context.Context, resourceGroup, virtualMachineScaleSet string) (compute.VirtualMachineScaleSetVMListResultPage, error) {
return az.virtualMachineScaleSetVMsClient.List(ctx, resourceGroup, virtualMachineScaleSet, "", "", "")
func (az *AzureClient) ListVirtualMachineScaleSetVMs(ctx context.Context, resourceGroup, virtualMachineScaleSet string) (VirtualMachineScaleSetVMListResultPage, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

page, err := az.virtualMachineScaleSetVMsClient.List(ctx, resourceGroup, virtualMachineScaleSet, "", "", "")
return &page, err
}

// DeleteVirtualMachineScaleSetVM deletes a VM in a VMSS
Expand Down
26 changes: 22 additions & 4 deletions pkg/armhelpers/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ type VirtualMachineListResultPage interface {
Values() []compute.VirtualMachine
}

// VirtualMachineScaleSetListResultPage is an interface for compute.VirtualMachineScaleSetListResultPage to aid in mocking
type VirtualMachineScaleSetListResultPage interface {
Next() error
NextWithContext(ctx context.Context) (err error)
NotDone() bool
Response() compute.VirtualMachineScaleSetListResult
Values() []compute.VirtualMachineScaleSet
}

// VirtualMachineScaleSetVMListResultPage is an interface for compute.VirtualMachineScaleSetListResultPage to aid in mocking
type VirtualMachineScaleSetVMListResultPage interface {
Next() error
NextWithContext(ctx context.Context) (err error)
NotDone() bool
Response() compute.VirtualMachineScaleSetVMListResult
Values() []compute.VirtualMachineScaleSetVM
}

// DeploymentOperationsListResultPage is an interface for resources.DeploymentOperationsListResultPage to aid in mocking
type DeploymentOperationsListResultPage interface {
Next() error
Expand Down Expand Up @@ -73,11 +91,11 @@ type AKSEngineClient interface {
// DeleteVirtualMachine deletes the specified virtual machine.
DeleteVirtualMachine(ctx context.Context, resourceGroup, name string) error

// ListVirtualMachineScaleSets lists the vmss resources in the resource group
ListVirtualMachineScaleSets(ctx context.Context, resourceGroup string) (compute.VirtualMachineScaleSetListResultPage, error)
// ListVirtualMachineScaleSets lists the VMSS resources in the resource group
ListVirtualMachineScaleSets(ctx context.Context, resourceGroup string) (VirtualMachineScaleSetListResultPage, error)

// ListVirtualMachineScaleSetVMs lists the virtual machines contained in a vmss
ListVirtualMachineScaleSetVMs(ctx context.Context, resourceGroup, virtualMachineScaleSet string) (compute.VirtualMachineScaleSetVMListResultPage, error)
// ListVirtualMachineScaleSetVMs lists the virtual machines contained in a VMSS
ListVirtualMachineScaleSetVMs(ctx context.Context, resourceGroup, virtualMachineScaleSet string) (VirtualMachineScaleSetVMListResultPage, error)

// DeleteVirtualMachineScaleSetVM deletes a VM in a VMSS
DeleteVirtualMachineScaleSetVM(ctx context.Context, resourceGroup, virtualMachineScaleSet, instanceID string) error
Expand Down
241 changes: 187 additions & 54 deletions pkg/armhelpers/mockclients.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,28 @@ import (

//MockAKSEngineClient is an implementation of AKSEngineClient where all requests error out
type MockAKSEngineClient struct {
FailDeployTemplate bool
FailDeployTemplateQuota bool
FailDeployTemplateConflict bool
FailDeployTemplateWithProperties bool
FailEnsureResourceGroup bool
FailListVirtualMachines bool
FailListVirtualMachinesTags bool
FailListVirtualMachineScaleSets bool
FailGetVirtualMachine bool
FailDeleteVirtualMachine bool
FailDeleteVirtualMachineScaleSetVM bool
FailSetVirtualMachineScaleSetCapacity bool
FailListVirtualMachineScaleSetVMs bool
FailGetStorageClient bool
FailDeleteNetworkInterface bool
FailGetKubernetesClient bool
FailListProviders bool
ShouldSupportVMIdentity bool
FailDeleteRoleAssignment bool
MockKubernetesClient *MockKubernetesClient
FailDeployTemplate bool
FailDeployTemplateQuota bool
FailDeployTemplateConflict bool
FailDeployTemplateWithProperties bool
FailEnsureResourceGroup bool
FailListVirtualMachines bool
FailListVirtualMachinesTags bool
FailListVirtualMachineScaleSets bool
FailGetVirtualMachine bool
FailDeleteVirtualMachine bool
FailDeleteVirtualMachineScaleSetVM bool
FailSetVirtualMachineScaleSetCapacity bool
FailListVirtualMachineScaleSetVMs bool
FailGetStorageClient bool
FailDeleteNetworkInterface bool
FailGetKubernetesClient bool
FailListProviders bool
ShouldSupportVMIdentity bool
FailDeleteRoleAssignment bool
MockKubernetesClient *MockKubernetesClient
FakeListVirtualMachineScaleSetsResult func() []compute.VirtualMachineScaleSet
FakeListVirtualMachineScaleSetVMsResult func() []compute.VirtualMachineScaleSetVM
}

//MockStorageClient mock implementation of StorageClient
Expand Down Expand Up @@ -105,6 +107,88 @@ func (page MockVirtualMachineListResultPage) Values() []compute.VirtualMachine {
return *page.Vmlr.Value
}

// MockVirtualMachineScaleSetListResultPage contains a page of VirtualMachine values.
type MockVirtualMachineScaleSetListResultPage struct {
Fn func(compute.VirtualMachineScaleSetListResult) (compute.VirtualMachineScaleSetListResult, error)
Vmsslr compute.VirtualMachineScaleSetListResult
}

// Next advances to the next page of values. If there was an error making
// the request the page does not advance and the error is returned.
func (page *MockVirtualMachineScaleSetListResultPage) Next() error {
next, err := page.Fn(page.Vmsslr)
if err != nil {
return err
}
page.Vmsslr = next
return nil
}

// NextWithContext advances to the next page of values. If there was an error making
// the request the page does not advance and the error is returned. context is ignored in the mock impl.
func (page *MockVirtualMachineScaleSetListResultPage) NextWithContext(context context.Context) error {
return page.Next()
}

// NotDone returns true if the page enumeration should be started or is not yet complete.
func (page MockVirtualMachineScaleSetListResultPage) NotDone() bool {
return !page.Vmsslr.IsEmpty()
}

// Response returns the raw server response from the last page request.
func (page MockVirtualMachineScaleSetListResultPage) Response() compute.VirtualMachineScaleSetListResult {
return page.Vmsslr
}

// Values returns the slice of values for the current page or nil if there are no values.
func (page MockVirtualMachineScaleSetListResultPage) Values() []compute.VirtualMachineScaleSet {
if page.Vmsslr.IsEmpty() {
return nil
}
return *page.Vmsslr.Value
}

// MockVirtualMachineScaleSetVMListResultPage contains a page of VMSS VirtualMachine values.
type MockVirtualMachineScaleSetVMListResultPage struct {
Fn func(compute.VirtualMachineScaleSetVMListResult) (compute.VirtualMachineScaleSetVMListResult, error)
Vmssvlr compute.VirtualMachineScaleSetVMListResult
}

// NextWithContext advances to the next page of values. If there was an error making
// the request the page does not advance and the error is returned. Context is ignored for the mock implementation
func (page *MockVirtualMachineScaleSetVMListResultPage) NextWithContext(ctx context.Context) error {
return page.Next()
}

// Next advances to the next page of values. If there was an error making
// the request the page does not advance and the error is returned.
func (page *MockVirtualMachineScaleSetVMListResultPage) Next() error {
next, err := page.Fn(page.Vmssvlr)
if err != nil {
return err
}
page.Vmssvlr = next
return nil
}

// NotDone returns true if the page enumeration should be started or is not yet complete.
func (page MockVirtualMachineScaleSetVMListResultPage) NotDone() bool {
return !page.Vmssvlr.IsEmpty()
}

// Response returns the raw server response from the last page request.
func (page MockVirtualMachineScaleSetVMListResultPage) Response() compute.VirtualMachineScaleSetVMListResult {
return page.Vmssvlr
}

// Values returns the slice of values for the current page or nil if there are no values.
func (page MockVirtualMachineScaleSetVMListResultPage) Values() []compute.VirtualMachineScaleSetVM {
if page.Vmssvlr.IsEmpty() {
return nil
}
return *page.Vmssvlr.Value
}

// MockDeploymentOperationsListResultPage contains a page of DeploymentOperation values.
type MockDeploymentOperationsListResultPage struct {
Fn func(resources.DeploymentOperationsListResult) (resources.DeploymentOperationsListResult, error)
Expand Down Expand Up @@ -371,6 +455,53 @@ func (mc *MockAKSEngineClient) ListVirtualMachines(ctx context.Context, resource
}, errors.New("ListVirtualMachines failed")
}

vm1 := mc.MakeFakeVirtualMachine()

vmr := compute.VirtualMachineListResult{}
vmr.Value = &[]compute.VirtualMachine{vm1}

return &MockVirtualMachineListResultPage{
Fn: func(lastResults compute.VirtualMachineListResult) (compute.VirtualMachineListResult, error) {
return compute.VirtualMachineListResult{}, nil
},
Vmlr: vmr,
}, nil
}

//ListVirtualMachineScaleSets mock
func (mc *MockAKSEngineClient) ListVirtualMachineScaleSets(ctx context.Context, resourceGroup string) (VirtualMachineScaleSetListResultPage, error) {
if mc.FailListVirtualMachineScaleSets {
return &MockVirtualMachineScaleSetListResultPage{}, errors.New("ListVirtualMachines failed")
}
if mc.FakeListVirtualMachineScaleSetsResult == nil {
//return 0 machined by default
mc.FakeListVirtualMachineScaleSetsResult = func() []compute.VirtualMachineScaleSet {
return []compute.VirtualMachineScaleSet{}
}
}

vmsslr := compute.VirtualMachineScaleSetListResult{}
vmss := mc.FakeListVirtualMachineScaleSetsResult()
vmsslr.Value = &vmss

return &MockVirtualMachineScaleSetListResultPage{
Fn: func(compute.VirtualMachineScaleSetListResult) (compute.VirtualMachineScaleSetListResult, error) {
return compute.VirtualMachineScaleSetListResult{}, nil
},
Vmsslr: vmsslr,
}, nil
}

//GetVirtualMachine mock
func (mc *MockAKSEngineClient) GetVirtualMachine(ctx context.Context, resourceGroup, name string) (compute.VirtualMachine, error) {
if mc.FailGetVirtualMachine {
return compute.VirtualMachine{}, errors.New("GetVirtualMachine failed")
}
return mc.MakeFakeVirtualMachine(), nil
}

// MakeFakeVirtualMachineScaleSetVM creates a fake VMSS VM
func (mc *MockAKSEngineClient) MakeFakeVirtualMachineScaleSetVM(orchestratorTag string) compute.VirtualMachineScaleSetVM {
vm1Name := "k8s-agentpool1-12345678-0"

creationSourceString := "creationSource"
Expand All @@ -379,24 +510,29 @@ func (mc *MockAKSEngineClient) ListVirtualMachines(ctx context.Context, resource
poolnameString := "poolName"

creationSource := "aksengine-k8s-agentpool1-12345678-0"
orchestrator := "Kubernetes:1.9.10"
orchestrator := orchestratorTag
resourceNameSuffix := "12345678"
poolname := "agentpool1"
computerName := "computerName"
instanceID := "someguidthatshouldbeunique"

tags := map[string]*string{
creationSourceString: &creationSource,
orchestratorString: &orchestrator,
resourceNameSuffixString: &resourceNameSuffix,
poolnameString: &poolname,
}

if mc.FailListVirtualMachinesTags {
tags = nil
}

vm1 := compute.VirtualMachine{
Name: &vm1Name,
Tags: tags,
VirtualMachineProperties: &compute.VirtualMachineProperties{
return compute.VirtualMachineScaleSetVM{
Name: &vm1Name,
Tags: tags,
InstanceID: &instanceID,
VirtualMachineScaleSetVMProperties: &compute.VirtualMachineScaleSetVMProperties{
OsProfile: &compute.OSProfile{ComputerName: &computerName},
StorageProfile: &compute.StorageProfile{
OsDisk: &compute.OSDisk{
Vhd: &compute.VirtualHardDisk{
Expand All @@ -412,33 +548,10 @@ func (mc *MockAKSEngineClient) ListVirtualMachines(ctx context.Context, resource
},
},
}

vmr := compute.VirtualMachineListResult{}
vmr.Value = &[]compute.VirtualMachine{vm1}

return &MockVirtualMachineListResultPage{
Fn: func(lastResults compute.VirtualMachineListResult) (compute.VirtualMachineListResult, error) {
return compute.VirtualMachineListResult{}, nil
},
Vmlr: vmr,
}, nil
}

//ListVirtualMachineScaleSets mock
func (mc *MockAKSEngineClient) ListVirtualMachineScaleSets(ctx context.Context, resourceGroup string) (compute.VirtualMachineScaleSetListResultPage, error) {
if mc.FailListVirtualMachineScaleSets {
return compute.VirtualMachineScaleSetListResultPage{}, errors.New("ListVirtualMachines failed")
}

return compute.VirtualMachineScaleSetListResultPage{}, nil
}

//GetVirtualMachine mock
func (mc *MockAKSEngineClient) GetVirtualMachine(ctx context.Context, resourceGroup, name string) (compute.VirtualMachine, error) {
if mc.FailGetVirtualMachine {
return compute.VirtualMachine{}, errors.New("GetVirtualMachine failed")
}

//MakeFakeVirtualMachine returns a fake compute.VirtualMachine
func (mc *MockAKSEngineClient) MakeFakeVirtualMachine() compute.VirtualMachine {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since this is already a mock function, using "fake" in the name is redundant. Also slight preference for mock over fake in comment wording.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I already commented before, the mockClient stubs functions like CreateVirtualMachine that are real function on the service. This is a helper builder function that return a default instance of a VirtualMachine. the Fake part of the name is to distinguish it from the functions exposed by the actual service that we mock

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you're saying existing MockAKSEngineClient methods (e.g., ListVirtualMachineScaleSets) represent mock equivalents of identically names methods in the real client libraries?

If that's the case, I'd argue we don't actually want to be adding these "fake" methods onto pre-existing mock libraries that fit that description, and better to create static functions that access the mock objects in order to return an appropriate fake, single-use object for testing.

I don't want to block progress, but something to think about as the mock + fake semantics are arguably a bit confusing.

Copy link
Member Author

@serbrech serbrech Feb 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I extracted this for reusability as it was inlined in the mockclient code and duplicated. I first wanted it standalone, but it relied on other parameters from the mockClient and so, I decided to keep it there for simplicity. it is used by the client to generate the faked results, and it can be used from the tests to build dummy objects.

vm1Name := "k8s-agentpool1-12345678-0"

creationSourceString := "creationSource"
Expand All @@ -465,6 +578,10 @@ func (mc *MockAKSEngineClient) GetVirtualMachine(ctx context.Context, resourceGr
vmIdentity = &compute.VirtualMachineIdentity{PrincipalID: &principalID}
}

if mc.FailListVirtualMachinesTags {
tags = nil
}

return compute.VirtualMachine{
Name: &vm1Name,
Tags: tags,
Expand All @@ -484,7 +601,7 @@ func (mc *MockAKSEngineClient) GetVirtualMachine(ctx context.Context, resourceGr
},
},
},
}, nil
}
}

//DeleteVirtualMachine mock
Expand Down Expand Up @@ -515,12 +632,28 @@ func (mc *MockAKSEngineClient) SetVirtualMachineScaleSetCapacity(ctx context.Con
}

//ListVirtualMachineScaleSetVMs mock
func (mc *MockAKSEngineClient) ListVirtualMachineScaleSetVMs(ctx context.Context, resourceGroup, virtualMachineScaleSet string) (compute.VirtualMachineScaleSetVMListResultPage, error) {
func (mc *MockAKSEngineClient) ListVirtualMachineScaleSetVMs(ctx context.Context, resourceGroup, virtualMachineScaleSet string) (VirtualMachineScaleSetVMListResultPage, error) {
if mc.FailDeleteVirtualMachineScaleSetVM {
return compute.VirtualMachineScaleSetVMListResultPage{}, errors.New("DeleteVirtualMachineScaleSetVM failed")
return &compute.VirtualMachineScaleSetVMListResultPage{}, errors.New("DeleteVirtualMachineScaleSetVM failed")
}

if mc.FakeListVirtualMachineScaleSetVMsResult == nil {
//return 0 machined by default
mc.FakeListVirtualMachineScaleSetVMsResult = func() []compute.VirtualMachineScaleSetVM {
return []compute.VirtualMachineScaleSetVM{}
}
}

return compute.VirtualMachineScaleSetVMListResultPage{}, nil
result := MockVirtualMachineScaleSetVMListResultPage{}
vms := mc.FakeListVirtualMachineScaleSetVMsResult()
result.Vmssvlr = compute.VirtualMachineScaleSetVMListResult{Value: &vms}

return &MockVirtualMachineScaleSetVMListResultPage{
Fn: func(compute.VirtualMachineScaleSetVMListResult) (compute.VirtualMachineScaleSetVMListResult, error) {
return compute.VirtualMachineScaleSetVMListResult{}, nil
},
Vmssvlr: compute.VirtualMachineScaleSetVMListResult{Value: &vms},
}, nil
}

//GetStorageClient mock
Expand Down