diff --git a/pkg/antctl/antctl.go b/pkg/antctl/antctl.go index 6f04c85b564..09d10d641ff 100644 --- a/pkg/antctl/antctl.go +++ b/pkg/antctl/antctl.go @@ -24,6 +24,7 @@ import ( "k8s.io/klog" "github.com/vmware-tanzu/antrea/pkg/antctl/handlers" + "github.com/vmware-tanzu/antrea/pkg/apis/clusterinformation/v1beta1" "github.com/vmware-tanzu/antrea/pkg/client/clientset/versioned/scheme" "github.com/vmware-tanzu/antrea/pkg/version" ) @@ -76,6 +77,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(v1beta1.AntreaAgentInfo{}), + 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..35fb7f283c8 --- /dev/null +++ b/pkg/antctl/handlers/agentinfo.go @@ -0,0 +1,46 @@ +// 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" + + "k8s.io/klog" + + "github.com/vmware-tanzu/antrea/pkg/apis/clusterinformation/v1beta1" + "github.com/vmware-tanzu/antrea/pkg/monitor" +) + +var _ Factory = new(AgentInfo) + +// 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 ComponentAgentInfoResponse. +func (v *AgentInfo) Handler(aq monitor.AgentQuerier, cq monitor.ControllerQuerier) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var m *v1beta1.AntreaAgentInfo + if aq != nil { + m = aq.GetAgentInfo() + } + err := json.NewEncoder(w).Encode(m) + 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..e29b3db9075 --- /dev/null +++ b/pkg/antctl/handlers/agentinfo_test.go @@ -0,0 +1,82 @@ +// 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" +) + +var antreaAgentInfo0 v1beta1.AntreaAgentInfo = v1beta1.AntreaAgentInfo{ + Version: "1.0", + PodRef: corev1.ObjectReference{ + Kind: "Pod", + Namespace: "kube-system", + Name: "antrea-agent-flx99", + }, + NodeRef: corev1.ObjectReference{ + Kind: "Node", + Name: "node0-k8s", + }, + 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 { + agentInfo *v1beta1.AntreaAgentInfo + expectedOutput string + expectedStatusCode int + }{ + "AgentInfo": { + agentInfo: &antreaAgentInfo0, + expectedOutput: "{\"metadata\":{\"creationTimestamp\":null},\"version\":\"1.0\",\"podRef\":{\"kind\":\"Pod\",\"namespace\":\"kube-system\",\"name\":\"antrea-agent-flx99\"},\"nodeRef\":{\"kind\":\"Node\",\"name\":\"node0-k8s\"},\"nodeSubnet\":[\"192.168.0.0/24\"],\"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/querier.go b/pkg/monitor/querier.go index 6458b3e58b3..606fb1296b8 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 { @@ -151,6 +152,16 @@ func (monitor *agentMonitor) GetAgentConditions(ovsConnected bool) []v1beta1.Age } } +// GetAgentInfo gets the antrea agent information. +func (monitor *agentMonitor) GetAgentInfo() *v1beta1.AntreaAgentInfo { + info, err := monitor.client.ClusterinformationV1beta1().AntreaAgentInfos().Get(monitor.GetSelfNode().Name, metav1.GetOptions{}) + if err != nil { + klog.Errorf("Failed to get agent information: %v", err) + return nil + } + return info +} + func (monitor *agentMonitor) GetVersion() string { return version.GetFullVersion() } 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()