Skip to content

Commit

Permalink
Implement antctl agent-info (#318)
Browse files Browse the repository at this point in the history
  • Loading branch information
lzhecheng authored Feb 21, 2020
1 parent adaa5f1 commit d742467
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 7 deletions.
11 changes: 11 additions & 0 deletions pkg/antctl/antctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
69 changes: 69 additions & 0 deletions pkg/antctl/handlers/agentinfo.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
87 changes: 87 additions & 0 deletions pkg/antctl/handlers/agentinfo_test.go
Original file line number Diff line number Diff line change
@@ -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)
})
}
}
18 changes: 11 additions & 7 deletions pkg/monitor/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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(),
Expand All @@ -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)
}
Expand Down
1 change: 1 addition & 0 deletions pkg/monitor/querier.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type AgentQuerier interface {
Querier
GetOVSFlowTable() map[string]int32
GetLocalPodNum() int32
GetAgentInfo() *v1beta1.AntreaAgentInfo
}

type ControllerQuerier interface {
Expand Down
14 changes: 14 additions & 0 deletions pkg/monitor/testing/mock_monitor.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit d742467

Please sign in to comment.