-
Notifications
You must be signed in to change notification settings - Fork 370
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 CLI command to get memberlist state #4611
Conversation
Codecov Report
@@ Coverage Diff @@
## main #4611 +/- ##
==========================================
- Coverage 69.78% 69.64% -0.15%
==========================================
Files 401 402 +1
Lines 59529 58388 -1141
==========================================
- Hits 41545 40662 -883
+ Misses 15166 14928 -238
+ Partials 2818 2798 -20
*This pull request uses carry forward flags. Click here to find out more.
|
ddc941b
to
c61a892
Compare
c61a892
to
4b9d035
Compare
Status string `json:"status,omitempty"` | ||
} | ||
|
||
func generateResponse(node v1.Node, aliveNodes sets.String) Response { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
func generateResponse(node v1.Node, aliveNodes sets.String) Response { | |
func generateResponse(node *v1.Node, aliveNodes sets.String) Response { |
to avoid copy
func HandleFunc(aq querier.AgentQuerier) http.HandlerFunc { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
var memberlist []Response | ||
allNodes, err := aq.GetK8sClient().CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The agent already caches all Nodes in memory, it could use NodeInformer's Lister to get all Nodes, intead of making an API call, which is not cheap in scale cluster.
return func(w http.ResponseWriter, r *http.Request) { | ||
var memberlist []Response | ||
allNodes, err := aq.GetK8sClient().CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After switching to NodeInformer, it would never fail, so error could be ignored.
pkg/agent/querier/querier.go
Outdated
@@ -55,6 +58,7 @@ type agentQuerier struct { | |||
networkPolicyInfoQuerier querier.AgentNetworkPolicyInfoQuerier | |||
apiPort int | |||
nplRange string | |||
memberlistCluster *memberlist.Cluster |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use Interface instead of concrete type
pkg/antctl/antctl.go
Outdated
aliases: []string{"ml"}, | ||
short: "Print state of memberlist cluster", | ||
long: "Print state of memberlist cluster of Antrea agent", | ||
example: ` Get a memberlist |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there is only one usage, this example looks verbose, could be deleted
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deleted, thanks.
4b9d035
to
ebb6519
Compare
node1 = v1.Node{ | ||
ObjectMeta: metav1.ObjectMeta{Name: "node1"}, | ||
Status: v1.NodeStatus{ | ||
Addresses: []v1.NodeAddress{ | ||
{ | ||
Address: "172.16.0.11", | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
expectedContent = []Response{ | ||
{ | ||
NodeName: "node1", | ||
IP: "172.16.0.11", | ||
Status: "Alive", | ||
}, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Move the expected response to the test to be self-contained. Expected response is not likely to be reused.
- It should include a dead node to cover the negaive case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
pkg/agent/querier/querier.go
Outdated
@@ -42,6 +45,8 @@ type AgentQuerier interface { | |||
GetOVSCtlClient() ovsctl.OVSCtlClient | |||
GetProxier() proxy.Proxier | |||
GetNetworkPolicyInfoQuerier() querier.AgentNetworkPolicyInfoQuerier | |||
GetAliveNodes() sets.String |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel the AgentQuerier is perhaps a bad interface which mixes too many things which breaks a few OOP rules. I understand the change is following existing code, however, it should use the same style to return a ClusterInterface and let the client to call its GetAliveNodes interface to be consistent, instead of playing a proxy here.
98474c0
to
187eff5
Compare
hack/update-codegen-dockerized.sh
Outdated
@@ -41,7 +41,7 @@ MOCKGEN_TARGETS=( | |||
"pkg/agent/cniserver/ipam IPAMDriver testing" | |||
"pkg/agent/flowexporter/connections ConnTrackDumper,NetFilterConnTrack testing" | |||
"pkg/agent/interfacestore InterfaceStore testing" | |||
"pkg/agent/memberlist Memberlist testing" | |||
"pkg/agent/memberlist Interface,Memberlist . mock_memberlist.go" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get why this change is made. Is it related to my comment here #4611 (comment)? If yes, I think it's misunderstood. I meant querier interface should provide a method to return memberlist.Interface, instead of providing a method GetAliveNodes and playing as a proxy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tnqn
This change is because :-
- I needed to mock
Interface
of memberlist package which hasAliveNodes
method in order to write unit test (pkg/agent/apiserver/handlers/memberlist/handler_test.go) [Line 75 and 78]. - Now, after mocking
Interface
of memberlist package intesting
sub package i was getting cyclic import issue, so to avoid that I created mock in the memberlist package itself. - Now, mock file was created as
mock_memberlist_test.go
, I changed that tomock_memberlist.go
inorder to make methods exportable as I needed it in unit test (handler_test.go).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you describe the cyclic import? I don't see it from the imports even if the file is in testing package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @tnqn
If we create mock for interfaces Memberlist
and Interface
in a file inside testing
subpackage, it would import "antrea.io/antrea/pkg/agent/memberlist" as it is required for mock of method AddClusterEventHandler
of interafce Interface
.
Now, we get cyclic import error in cluster.go and cluster_test.go as cluster_test.go imports this testing
subpackage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, now I see there is a MockMemberlist in the testing package. Normally the testing has a mock implementation of the interface its parent package provides, and it's intended to be used by the interface's consumers in their tests, and the parent package wouldn't consume its own testing (and the testing package will likely consume the parent package because of the type import).
However, just moving the two mock structs to the parent package would cause other "problems":
If the mock structs are put in a file with "_test.go" suffix, they won't be exported and we can't use MockInterface.
If the mock structs are put in a file without "_test.go" suffix, they will be compiled and linked into the product code unnecessarily.
I would suggest to follow the mode that keeps the mock implementation of the target interface in testing package, and keeps the mock implementation of the dependent interface in "mock_memberlist_test.go" in the parent package as it's not intended to be consumed out of the package.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tnqn done, thanks for the suggestion.
187eff5
to
161996e
Compare
clientset := fake.NewSimpleClientset() | ||
informerFactory := informers.NewSharedInformerFactory(clientset, 0) | ||
nodeInformer := informerFactory.Core().V1().Nodes() | ||
|
||
stopCh := make(chan struct{}) | ||
defer close(stopCh) | ||
|
||
informerFactory.Start(stopCh) | ||
informerFactory.WaitForCacheSync(stopCh) | ||
informerFactory.Core().V1().Nodes().Informer().GetIndexer().Add(&node1) | ||
informerFactory.Core().V1().Nodes().Informer().GetIndexer().Add(&node2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks neater to pass the two objects when creating the clientset. It needs an "informer" or "lister" to be created first by calling nodeInformer.Informer()
or nodeInformer.Lister()
. But I feel AgentQuerier just needs NodeLister so maybe change the passed type to resolve the it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done, thanks!
sort.Slice(received, func(i, j int) bool { | ||
return received[i].NodeName < received[j].NodeName | ||
}) | ||
assert.Equal(t, expectedResponse, received) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
assert.ElementsMatch
Add antrea agent command `antctl get memberlist` to get state of memberlist cluster of antrea agent. Fixes antrea-io#4601 Signed-off-by: Kumar Atish <[email protected]>
161996e
to
6366fd5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks
/test-all |
/skip-e2e which failed on known flaky test |
|
||
### Showing memberlist state | ||
|
||
`antctl` agent command `get memberlist` (or `get ml`) print the state of memberlist cluster of Antrea Agent. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
print -> prints
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add an antrea agent command
antctl get memberlist
to get the state of memberlist cluster of antrea-agent.Fixes #4601
Signed-off-by: Kumar Atish [email protected]