Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mocking to Cyclops #85

Merged
merged 8 commits into from
Aug 12, 2024
Merged

Conversation

vincentportella
Copy link
Member

  • Adding a new mocking package to help test individual phases of Cyclops in a repeatable manner. This PR only includes tests for the Pending phase and includes the required functionality for that.
  • Fixing an issue where if no nodegroups names are given then all nodegroups are determined to be matching.

@vincentportella vincentportella self-assigned this Aug 9, 2024
@@ -40,3 +42,11 @@ func NewCloudProvider(logger logr.Logger) (cloudprovider.CloudProvider, error) {

return p, nil
}

// NewMockCloudProvider returns a new mock AWS cloud provider
func NewMockCloudProvider(autoscalingiface autoscalingiface.AutoScalingAPI, ec2iface ec2iface.EC2API) cloudprovider.CloudProvider {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this really returning a new mock AWS cloud provider or just a new provider that could either be the real thing or mocked depending on what is passed in?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I think this doesn't have to be NewMockCloudProvider but could just be NewCloudProvider where the two interfaces passed in are the mocks.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, it's not necessarily mocking.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this an appropriate location in the codebase for testing the Pending phase? I'd have thought these tests would be better located with the phase implementations themselves.

Copy link
Collaborator

Choose a reason for hiding this comment

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

IMO the mock package should only contain things to do with creating / setting up / tearing down mocks. Tests themselves belong with the code file that implements them.

Copy link
Member Author

Choose a reason for hiding this comment

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

Unfortunately that leads to an import cycle so this is the best place I could think of putting it

Copy link
Collaborator

Choose a reason for hiding this comment

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

The mock for an individual piece of functionality can live next to the code too, I've seen this happen in quite a few code bases. That avoids the import cycle. You just import generic mocking functionality from the central package in that situation.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Unfortunately that leads to an import cycle so this is the best place I could think of putting it

If this is occurring and as @mwhittington21 suggests we'll need to make the mocking package more generic and moving mocking logic to where it makes sense (probably next to the thing it is mocking)

},
},
NodeNames: []string{
"node-0",
Copy link
Collaborator

Choose a reason for hiding this comment

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

I guess one nit here is in this test we're assuming that when you create a test node group with newNodegroup() that nodes follow this naming scheme.

Someone modifying newNodegroup() may find they break a ton of tests if they decide to change an assumption on how test node groups are created

Copy link
Collaborator

Choose a reason for hiding this comment

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

You could possibly remedy this by adding a node that obviously would very obviously never exist in the NodeNames field

assert.NoError(t, err)
assert.Equal(t, v1.CycleNodeRequestPending, mockTransitioner.Cnr.Status.Phase)

// Simulate waiting for 1 day, this is more than the waiting limit
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are we assuming the waiting limit is always < 24 hours? Can we make this an input into the test, so that someone changing the wait limit doesn't break unrelated tests

}

// SetDesiredCapacity mock implementation for MockAutoscalingService
func (m MockAutoscalingService) SetDesiredCapacity(*autoscaling.SetDesiredCapacityInput) (*autoscaling.SetDesiredCapacityOutput, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I notice we've not implemented functions like this in the new mock AWS - do we not need them?

Copy link
Member Author

Choose a reason for hiding this comment

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

I will implement functionality as needed. As I add new features in upcoming PRs I will add the functionality for the mocking to support the appropriate tests.

pkg/mock/util.go Outdated
Copy link
Collaborator

Choose a reason for hiding this comment

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

Call this file test_helpers.go or something so more things don't pile into it over time. util.go is likely to attract more cruft.

@@ -40,3 +42,11 @@ func NewCloudProvider(logger logr.Logger) (cloudprovider.CloudProvider, error) {

return p, nil
}

// NewMockCloudProvider returns a new mock AWS cloud provider
func NewMockCloudProvider(autoscalingiface autoscalingiface.AutoScalingAPI, ec2iface ec2iface.EC2API) cloudprovider.CloudProvider {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah I think this doesn't have to be NewMockCloudProvider but could just be NewCloudProvider where the two interfaces passed in are the mocks.

Copy link
Collaborator

Choose a reason for hiding this comment

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

IMO the mock package should only contain things to do with creating / setting up / tearing down mocks. Tests themselves belong with the code file that implements them.

pkg/mock/util.go Outdated
}

node := &Node{
Name: fmt.Sprintf("node-%d", i),
Copy link
Collaborator

Choose a reason for hiding this comment

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

If I create two NodeGroups for a test, is there a problem if the groups contain the same named nodes? With the current implementation this will happen. I suspect this is not intentional, and the node names should probably be generated such that they aren't shared between NodeGroups.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point, I can add the nodegroup name to the node name to help get around this.

pkg/mock/util.go Outdated
nodes := make([]*Node, 0)

for i := 0; i < num; i++ {
instanceID, err := generateRandomInstanceId()
Copy link
Collaborator

Choose a reason for hiding this comment

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

If you're ever doing a test that wants to look for specific instance IDs you will want a way to customise this. Not sure if it's worth adding now or not.

Copy link
Member Author

@vincentportella vincentportella Aug 9, 2024

Choose a reason for hiding this comment

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

This is more of a helper, instance IDs can be made custom when someone would want to create a node without it.

return nil
}

func generateFakeInstances(nodes []*Node) *[]*fakeaws.Instance {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is there a reason to returning a pointer to a slice?

type Ec2 struct {
ec2iface.EC2API

Instances *[]*Instance
Copy link
Collaborator

Choose a reason for hiding this comment

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

Related to my other comment on pointer to a slice - why?

Copy link
Member Author

@vincentportella vincentportella Aug 12, 2024

Choose a reason for hiding this comment

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

It was to take into terminating instances using the ec2 instance and then describing asgs using the autoscaling instance and I want them to be in sync. I've done some testing and changed this to a map to make it easier to manage those fake instances in the future and it works as I'd like.

@vincentportella vincentportella merged commit 81491a7 into master Aug 12, 2024
3 checks passed
@vincentportella vincentportella deleted the vportella/add-mocking-to-cyclops branch August 12, 2024 06:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants