From c4ffddaeb3abd5c043c22d5b3c6b21b088ea7930 Mon Sep 17 00:00:00 2001 From: Zhecheng Li Date: Mon, 13 Jan 2020 01:40:18 -0800 Subject: [PATCH] Implement antctl agent-info --- pkg/antctl/antctl.go | 11 ++++ pkg/antctl/handlers/agentinfo.go | 69 +++++++++++++++++++++ pkg/antctl/handlers/agentinfo_test.go | 87 +++++++++++++++++++++++++++ pkg/monitor/monitor.go | 18 +++--- pkg/monitor/querier.go | 1 + pkg/monitor/testing/mock_monitor.go | 14 +++++ 6 files changed, 193 insertions(+), 7 deletions(-) create mode 100644 pkg/antctl/handlers/agentinfo.go create mode 100644 pkg/antctl/handlers/agentinfo_test.go diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go index 6f04c85b564..5a04f213067 100644 --- a/pkg/antctl/antctl.go +++ b/pkg/antctl/antctl.go @@ -76,6 +76,17 @@ var CommandList = &commandList{ CommandGroup: flat, AddonTransform: versionTransform, }, + { + Use: "agent-info", + Short: "Print agent's basic information", + Long: "Print agent's basic information including version, node subnet, OVS info, AgentConditions, etc.", + HandlerFactory: new(handlers.AgentInfo), + GroupVersion: &systemGroup, + TransformedResponse: reflect.TypeOf(handlers.AntreaAgentInfoResponse{}), + Agent: true, + SingleObject: true, + CommandGroup: flat, + }, }, codec: scheme.Codecs, } diff --git a/pkg/antctl/handlers/agentinfo.go b/pkg/antctl/handlers/agentinfo.go new file mode 100644 index 00000000000..d99ee7b6563 --- /dev/null +++ b/pkg/antctl/handlers/agentinfo.go @@ -0,0 +1,69 @@ +// Copyright 2020 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package handlers + +import ( + "encoding/json" + "net/http" + + corev1 "k8s.io/api/core/v1" + "k8s.io/klog" + + "github.com/vmware-tanzu/antrea/pkg/apis/clusterinformation/v1beta1" + "github.com/vmware-tanzu/antrea/pkg/monitor" +) + +var _ Factory = new(AgentInfo) + +// AntreaAgentInfoResponse is the struct for the response of agent-info command. +// It includes all fields except meta info from v1beta1.AntreaAgentInfo struct. +type AntreaAgentInfoResponse struct { + Version string `json:"version,omitempty"` // Antrea binary version + PodRef corev1.ObjectReference `json:"podRef,omitempty"` // The Pod that Antrea Agent is running in + NodeRef corev1.ObjectReference `json:"nodeRef,omitempty"` // The Node that Antrea Agent is running in + NodeSubnet []string `json:"nodeSubnet,omitempty"` // Node subnet + OVSInfo v1beta1.OVSInfo `json:"ovsInfo,omitempty"` // OVS Information + NetworkPolicyControllerInfo v1beta1.NetworkPolicyControllerInfo `json:"networkPolicyControllerInfo,omitempty"` // Antrea Agent NetworkPolicy information + LocalPodNum int32 `json:"localPodNum,omitempty"` // The number of Pods which the agent is in charge of + AgentConditions []v1beta1.AgentCondition `json:"agentConditions,omitempty"` // Agent condition contains types like AgentHealthy +} + +// AgentInfo is the implementation of the Factory interface for the agent-info command. +type AgentInfo struct{} + +// Handler returns the function which can handle queries issued by agent-info commands, +// the handler function populate component's agent-info to the response. +func (v *AgentInfo) Handler(aq monitor.AgentQuerier, cq monitor.ControllerQuerier) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var info *AntreaAgentInfoResponse + if aq != nil { + allInfo := aq.GetAgentInfo() + info = &AntreaAgentInfoResponse{ + Version: allInfo.Version, + PodRef: allInfo.PodRef, + NodeRef: allInfo.NodeRef, + OVSInfo: allInfo.OVSInfo, + NetworkPolicyControllerInfo: allInfo.NetworkPolicyControllerInfo, + LocalPodNum: allInfo.LocalPodNum, + AgentConditions: allInfo.AgentConditions, + } + } + err := json.NewEncoder(w).Encode(info) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + klog.Errorf("Error when encoding AntreaAgentInfo to json: %v", err) + } + } +} diff --git a/pkg/antctl/handlers/agentinfo_test.go b/pkg/antctl/handlers/agentinfo_test.go new file mode 100644 index 00000000000..998e046df01 --- /dev/null +++ b/pkg/antctl/handlers/agentinfo_test.go @@ -0,0 +1,87 @@ +// Copyright 2020 Antrea Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package handlers + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/vmware-tanzu/antrea/pkg/apis/clusterinformation/v1beta1" + mockmonitor "github.com/vmware-tanzu/antrea/pkg/monitor/testing" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +var antreaAgentInfo0 v1beta1.AntreaAgentInfo = v1beta1.AntreaAgentInfo{ + ObjectMeta: metav1.ObjectMeta{ + Name: "node0-k8", + }, + Version: "1.0", + PodRef: corev1.ObjectReference{ + Kind: "Pod", + Namespace: "kube-system", + Name: "antrea-agent-flx99", + }, + NodeRef: corev1.ObjectReference{ + Kind: "Node", + Name: "node0-k8", + }, + NodeSubnet: []string{ + "192.168.0.0/24", + }, + OVSInfo: v1beta1.OVSInfo{}, + NetworkPolicyControllerInfo: v1beta1.NetworkPolicyControllerInfo{}, + LocalPodNum: 1, + AgentConditions: []v1beta1.AgentCondition{ + { + Type: v1beta1.AgentHealthy, + Status: corev1.ConditionTrue, + }, + }, +} + +func TestAgentInfo(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + testcases := map[string]struct { + testNode corev1.ObjectReference + agentInfo *v1beta1.AntreaAgentInfo + expectedOutput string + expectedStatusCode int + }{ + "AgentInfo": { + agentInfo: &antreaAgentInfo0, + expectedOutput: "{\"version\":\"1.0\",\"podRef\":{\"kind\":\"Pod\",\"namespace\":\"kube-system\",\"name\":\"antrea-agent-flx99\"},\"nodeRef\":{\"kind\":\"Node\",\"name\":\"node0-k8\"},\"ovsInfo\":{},\"networkPolicyControllerInfo\":{},\"localPodNum\":1,\"agentConditions\":[{\"type\":\"AgentHealthy\",\"status\":\"True\",\"lastHeartbeatTime\":null}]}\n", + expectedStatusCode: http.StatusOK, + }, + } + for k, tc := range testcases { + t.Run(k, func(t *testing.T) { + req, err := http.NewRequest("GET", "/", nil) + assert.Nil(t, err) + recorder := httptest.NewRecorder() + aq := mockmonitor.NewMockAgentQuerier(ctrl) + aq.EXPECT().GetAgentInfo().Return(tc.agentInfo) + new(AgentInfo).Handler(aq, nil).ServeHTTP(recorder, req) + assert.Equal(t, tc.expectedStatusCode, recorder.Code, k) + assert.Equal(t, tc.expectedOutput, recorder.Body.String(), k) + }) + } +} diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index 0a1af402d14..28bb503ea73 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -126,13 +126,12 @@ func (monitor *controllerMonitor) Run(stopCh <-chan struct{}) { // Then updates AntreaAgentInfo CRD every 60 seconds. func (monitor *agentMonitor) Run(stopCh <-chan struct{}) { klog.Info("Starting Antrea Agent Monitor") - crdName := monitor.GetSelfNode().Name - agentCRD := monitor.getAgentCRD(crdName) + agentCRD := monitor.getAgentCRD() var err error = nil // Initialize agent monitoring CRD. if agentCRD == nil { - agentCRD, err = monitor.createAgentCRD(crdName) + agentCRD, err = monitor.createAgentCRD() if err != nil { klog.Errorf("Failed to create agent monitoring CRD %v : %v", agentCRD, err) return @@ -255,7 +254,8 @@ func (monitor *controllerMonitor) deleteAgentCRD(name string) { // getAgentCRD is used to check the existence of agent monitoring CRD. // So when the pod restarts, it will update this monitoring CRD instead of creating a new one. -func (monitor *agentMonitor) getAgentCRD(crdName string) *v1beta1.AntreaAgentInfo { +func (monitor *agentMonitor) getAgentCRD() *v1beta1.AntreaAgentInfo { + crdName := monitor.nodeName agentCRD, err := monitor.client.ClusterinformationV1beta1().AntreaAgentInfos().Get(crdName, metav1.GetOptions{}) if err != nil { klog.V(2).Infof("Agent monitoring CRD named %s doesn't exist, will create one", crdName) @@ -264,12 +264,12 @@ func (monitor *agentMonitor) getAgentCRD(crdName string) *v1beta1.AntreaAgentInf return agentCRD } -func (monitor *agentMonitor) createAgentCRD(crdName string) (*v1beta1.AntreaAgentInfo, error) { +func (monitor *agentMonitor) GetAgentInfo() *v1beta1.AntreaAgentInfo { ovsVersion := monitor.GetOVSVersion() ovsConnected := ovsVersion != "" - agentCRD := &v1beta1.AntreaAgentInfo{ + return &v1beta1.AntreaAgentInfo{ ObjectMeta: metav1.ObjectMeta{ - Name: crdName, + Name: monitor.nodeName, }, Version: version.GetFullVersion(), PodRef: monitor.GetSelfPod(), @@ -280,6 +280,10 @@ func (monitor *agentMonitor) createAgentCRD(crdName string) (*v1beta1.AntreaAgen LocalPodNum: monitor.GetLocalPodNum(), AgentConditions: monitor.GetAgentConditions(ovsConnected), } +} + +func (monitor *agentMonitor) createAgentCRD() (*v1beta1.AntreaAgentInfo, error) { + agentCRD := monitor.GetAgentInfo() klog.V(2).Infof("Creating agent monitoring CRD %v", agentCRD) return monitor.client.ClusterinformationV1beta1().AntreaAgentInfos().Create(agentCRD) } diff --git a/pkg/monitor/querier.go b/pkg/monitor/querier.go index 6458b3e58b3..0cc7cfad50d 100644 --- a/pkg/monitor/querier.go +++ b/pkg/monitor/querier.go @@ -45,6 +45,7 @@ type AgentQuerier interface { Querier GetOVSFlowTable() map[string]int32 GetLocalPodNum() int32 + GetAgentInfo() *v1beta1.AntreaAgentInfo } type ControllerQuerier interface { diff --git a/pkg/monitor/testing/mock_monitor.go b/pkg/monitor/testing/mock_monitor.go index c8a73e90598..f5c4c4c688d 100644 --- a/pkg/monitor/testing/mock_monitor.go +++ b/pkg/monitor/testing/mock_monitor.go @@ -49,6 +49,20 @@ func (m *MockAgentQuerier) EXPECT() *MockAgentQuerierMockRecorder { return m.recorder } +// GetAgentInfo mocks base method +func (m *MockAgentQuerier) GetAgentInfo() *v1beta1.AntreaAgentInfo { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAgentInfo") + ret0, _ := ret[0].(*v1beta1.AntreaAgentInfo) + return ret0 +} + +// GetAgentInfo indicates an expected call of GetAgentInfo +func (mr *MockAgentQuerierMockRecorder) GetAgentInfo() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAgentInfo", reflect.TypeOf((*MockAgentQuerier)(nil).GetAgentInfo)) +} + // GetLocalPodNum mocks base method func (m *MockAgentQuerier) GetLocalPodNum() int32 { m.ctrl.T.Helper()