Skip to content

Commit

Permalink
Improve antctl get featuregates output (#5314)
Browse files Browse the repository at this point in the history
Fixes #5173

Here are the major changes in this PR:

1. Keep the information about which component is using each FeatureGate in antrea_features.go. We use this information to ensure correctness when retrieving Feature Gate values: no FeatureGate should be missing, FeatureGates should be displayed for the relevant component only.
2. Add support for antrea-agent-windows.

Signed-off-by: chengzw <[email protected]>
Signed-off-by: Antonin Bas <[email protected]>
Co-authored-by: Antonin Bas <[email protected]>
  • Loading branch information
cr7258 and antoninbas authored Sep 20, 2023
1 parent 0175b9a commit e16c364
Show file tree
Hide file tree
Showing 8 changed files with 936 additions and 192 deletions.
35 changes: 19 additions & 16 deletions pkg/agent/apiserver/handlers/featuregates/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package featuregates
import (
"encoding/json"
"net/http"
"sort"

"k8s.io/klog/v2"

Expand All @@ -32,31 +33,33 @@ type Response struct {

// HandleFunc returns the function which can handle queries issued by 'antctl get featuregates' command.
// The handler function populates Antrea Agent feature gates information to the response.
// For now, it will return all feature gates included in features.DefaultAntreaFeatureGates for agent
// We need to exclude any new feature gates which is not consumed by agent in the future.
func HandleFunc() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var featureGates []Response
status, version := "", ""
for df := range features.DefaultAntreaFeatureGates {
if features.DefaultMutableFeatureGate.Enabled(df) {
status = "Enabled"
} else {
status = "Disabled"
if features.AgentGates.Has(df) {
featureGates = append(featureGates, Response{
Component: "agent",
Name: string(df),
Status: getStatus(features.DefaultFeatureGate.Enabled(df)),
Version: string(features.DefaultAntreaFeatureGates[df].PreRelease),
})
}
version = string(features.DefaultAntreaFeatureGates[df].PreRelease)
featureGates = append(featureGates, Response{
Component: "agent",
Name: string(df),
Status: status,
Version: version,
})
}

sort.Slice(featureGates, func(i, j int) bool {
return featureGates[i].Name < featureGates[j].Name
})
err := json.NewEncoder(w).Encode(featureGates)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
klog.Errorf("Error when encoding FeatureGates to json: %v", err)
klog.ErrorS(err, "Error when encoding FeatureGates to json")
}
}
}

func getStatus(status bool) string {
if status {
return "Enabled"
}
return "Disabled"
}
54 changes: 54 additions & 0 deletions pkg/agent/apiserver/handlers/featuregates/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2021 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 featuregates

import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/component-base/featuregate"

"antrea.io/antrea/pkg/features"
)

func TestGetStatus(t *testing.T) {
assert.Equal(t, "Enabled", getStatus(true))
assert.Equal(t, "Disabled", getStatus(false))
}

func TestHandleFunc(t *testing.T) {
handler := HandleFunc()
req, err := http.NewRequest(http.MethodGet, "", nil)
require.Nil(t, err)

recorder := httptest.NewRecorder()
handler.ServeHTTP(recorder, req)
require.Equal(t, http.StatusOK, recorder.Code)

var resp []Response
err = json.Unmarshal(recorder.Body.Bytes(), &resp)
require.Nil(t, err)

for _, v := range resp {
df, ok := features.DefaultAntreaFeatureGates[featuregate.Feature(v.Name)]
require.True(t, ok)
assert.Equal(t, v.Status, getStatus(df.Default))
assert.Equal(t, v.Version, string(df.PreRelease))
}
}
39 changes: 29 additions & 10 deletions pkg/antctl/raw/featuregates/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,20 +87,26 @@ func featureGateRequest(cmd *cobra.Command, mode string) error {
return err
}
var agentGates []featuregates.Response
var agentWindowsGates []featuregates.Response
var controllerGates []featuregates.Response
for _, v := range resp {
if v.Component == "agent" {
switch v.Component {
case featuregates.AgentMode:
agentGates = append(agentGates, v)
} else {
case featuregates.AgentWindowsMode:
agentWindowsGates = append(agentWindowsGates, v)
case featuregates.ControllerMode:
controllerGates = append(controllerGates, v)
}
}
if len(agentGates) > 0 {
output(agentGates, runtime.ModeAgent, cmd.OutOrStdout())
output(agentGates, featuregates.AgentMode, cmd.OutOrStdout())
}
if len(agentWindowsGates) > 0 {
output(agentWindowsGates, featuregates.AgentWindowsMode, cmd.OutOrStdout())
}
if len(controllerGates) > 0 {
fmt.Println()
output(controllerGates, runtime.ModeController, cmd.OutOrStdout())
output(controllerGates, featuregates.ControllerMode, cmd.OutOrStdout())
}
return nil
}
Expand Down Expand Up @@ -165,14 +171,27 @@ func getFeatureGatesRequest(client *rest.RESTClient) ([]featuregates.Response, e
return resp, nil
}

func output(resps []featuregates.Response, runtimeMode string, output io.Writer) {
switch runtimeMode {
case runtime.ModeAgent:
func output(resps []featuregates.Response, component string, output io.Writer) {
switch component {
case featuregates.AgentMode:
output.Write([]byte("Antrea Agent Feature Gates\n"))
case runtime.ModeController:
case featuregates.AgentWindowsMode:
output.Write([]byte("\n"))
output.Write([]byte("Antrea Agent Feature Gates (Windows)\n"))
case featuregates.ControllerMode:
output.Write([]byte("\n"))
output.Write([]byte("Antrea Controller Feature Gates\n"))
}
formatter := "%-25s%-15s%-10s\n"

maxNameLen := len("FEATUREGATE")
maxStatusLen := len("STATUS")

for _, r := range resps {
maxNameLen = max(maxNameLen, len(r.Name))
maxStatusLen = max(maxStatusLen, len(r.Status))
}

formatter := fmt.Sprintf("%%-%ds%%-%ds%%-s\n", maxNameLen+5, maxStatusLen+5)
output.Write([]byte(fmt.Sprintf(formatter, "FEATUREGATE", "STATUS", "VERSION")))
for _, r := range resps {
fmt.Fprintf(output, formatter, r.Name, r.Status, r.Version)
Expand Down
Loading

0 comments on commit e16c364

Please sign in to comment.