Skip to content

Commit

Permalink
Feature command (#12014)
Browse files Browse the repository at this point in the history
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description

- add features command 

<!-- Issue number if applicable -->
#### Link to tracking issue
- #11998

<!--Describe what testing was performed and which tests were added.-->
#### Testing
- test with `make`
- test with `make test`
<!--Describe the documentation added.-->
#### Documentation

<!--Please delete paragraphs that you did not use before submitting.-->

---------

Signed-off-by: danish9039 <danishsiddiqui040@gmail.com>
Co-authored-by: Alex Boten <223565+codeboten@users.noreply.github.com>
  • Loading branch information
danish9039 and codeboten authored Feb 5, 2025
1 parent fb06c99 commit 0294890
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 0 deletions.
13 changes: 13 additions & 0 deletions .chloggen/featuregate-command.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
change_type: enhancement

component: otelcol

note: "Add featuregate command to display information about available features"

issues: [11998]

subtext: |
The featuregate command allows users to view detailed information about feature gates
including their status, stage, and description.
change_logs: [user]
45 changes: 45 additions & 0 deletions otelcol/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ package otelcol // import "go.opentelemetry.io/collector/otelcol"
import (
"errors"
"flag"
"fmt"
"os"
"text/tabwriter"

"github.com/spf13/cobra"

Expand Down Expand Up @@ -36,6 +39,7 @@ func NewCommand(set CollectorSettings) *cobra.Command {
return col.Run(cmd.Context())
},
}
rootCmd.AddCommand(newFeatureGateCommand())
rootCmd.AddCommand(newComponentsCommand(set))
rootCmd.AddCommand(newValidateSubCommand(set, flagSet))
rootCmd.Flags().AddGoFlagSet(flagSet)
Expand Down Expand Up @@ -63,3 +67,44 @@ func updateSettingsUsingFlags(set *CollectorSettings, flags *flag.FlagSet) error
}
return nil
}

func newFeatureGateCommand() *cobra.Command {
return &cobra.Command{
Use: "featuregate [feature-id]",
Short: "Display feature gates information",
Long: "Display information about available feature gates and their status",
RunE: func(_ *cobra.Command, args []string) error {
if len(args) > 0 {
found := false
featuregate.GlobalRegistry().VisitAll(func(g *featuregate.Gate) {
if g.ID() == args[0] {
found = true
fmt.Printf("Feature: %s\n", g.ID())
fmt.Printf("Enabled: %v\n", g.IsEnabled())
fmt.Printf("Stage: %s\n", g.Stage())
fmt.Printf("Description: %s\n", g.Description())
fmt.Printf("From Version: %s\n", g.FromVersion())
if g.ToVersion() != "" {
fmt.Printf("To Version: %s\n", g.ToVersion())
}
}
})
if !found {
return fmt.Errorf("feature %q not found", args[0])
}
return nil
}

w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "ID\tEnabled\tStage\tDescription\n")
featuregate.GlobalRegistry().VisitAll(func(g *featuregate.Gate) {
fmt.Fprintf(w, "%s\t%v\t%s\t%s\n",
g.ID(),
g.IsEnabled(),
g.Stage(),
g.Description())
})
return w.Flush()
},
}
}
58 changes: 58 additions & 0 deletions otelcol/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ package otelcol

import (
"context"
"io"
"os"
"path/filepath"
"testing"

Expand Down Expand Up @@ -159,3 +161,59 @@ func Test_UseUnifiedEnvVarExpansionRules(t *testing.T) {
})
}
}

func TestNewFeatureGateCommand(t *testing.T) {
t.Run("list all featuregates", func(t *testing.T) {
cmd := newFeatureGateCommand()
require.NotNil(t, cmd)

// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

err := cmd.RunE(cmd, []string{})
require.NoError(t, err)

w.Close()
out, _ := io.ReadAll(r)
os.Stdout = oldStdout

output := string(out)
assert.Contains(t, output, "ID")
assert.Contains(t, output, "Enabled")
assert.Contains(t, output, "Stage")
assert.Contains(t, output, "Description")
})
t.Run("specific featuregate details", func(t *testing.T) {
cmd := newFeatureGateCommand()

// Register a test feature gate in the global registry
featuregate.GlobalRegistry().MustRegister("test.feature", featuregate.StageBeta,
featuregate.WithRegisterDescription("Test feature description"))

// Capture stdout
oldStdout := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w

err := cmd.RunE(cmd, []string{"test.feature"})
require.NoError(t, err)

w.Close()
out, _ := io.ReadAll(r)
os.Stdout = oldStdout

output := string(out)
assert.Contains(t, output, "Feature: test.feature")
assert.Contains(t, output, "Description: Test feature description")
assert.Contains(t, output, "Stage: Beta")
})

t.Run("non-existent featuregate", func(t *testing.T) {
cmd := newFeatureGateCommand()
err := cmd.RunE(cmd, []string{"non.existent.feature"})
require.Error(t, err)
assert.Contains(t, err.Error(), "feature \"non.existent.feature\" not found")
})
}

0 comments on commit 0294890

Please sign in to comment.