Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to use feature-gates in the operator #1619

Merged
merged 3 commits into from
Apr 4, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .chloggen/add-featuregates.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. operator, target allocator, github action)
component: operator

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add ability to use feature gates in the operator

# One or more tracking issues related to the change
issues: [1619]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/prometheus/prometheus v1.8.2-0.20210621150501-ff58416a0b02
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.2
go.opentelemetry.io/collector/featuregate v0.74.0
go.opentelemetry.io/otel v1.14.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.26.3
Expand Down Expand Up @@ -109,7 +110,7 @@ require (
go.opencensus.io v0.23.0 // indirect
go.opentelemetry.io/otel/trace v1.14.0 // indirect
go.uber.org/atomic v1.8.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/multierr v1.10.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.1.0 // indirect
golang.org/x/net v0.7.0 // indirect
Expand Down
5 changes: 4 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opentelemetry.io/collector/featuregate v0.74.0 h1:hzkzhi6pvjqEK5+CkVBJX69wpEEYqgtTFMHGlZFsQyE=
go.opentelemetry.io/collector/featuregate v0.74.0/go.mod h1:pmVMr98Ps6QKyEHiVPN7o3Qd8K//M2NapfOv5BMWvA0=
go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
Expand All @@ -1057,8 +1059,9 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.4.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
Expand Down
4 changes: 4 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

routev1 "github.com/openshift/api/route/v1"
"github.com/spf13/pflag"
"go.opentelemetry.io/collector/featuregate"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sruntime "k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
Expand All @@ -47,6 +48,7 @@ import (
"github.com/open-telemetry/opentelemetry-operator/internal/webhookhandler"
"github.com/open-telemetry/opentelemetry-operator/pkg/autodetect"
collectorupgrade "github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade"
"github.com/open-telemetry/opentelemetry-operator/pkg/flags"
"github.com/open-telemetry/opentelemetry-operator/pkg/instrumentation"
instrumentationupgrade "github.com/open-telemetry/opentelemetry-operator/pkg/instrumentation/upgrade"
"github.com/open-telemetry/opentelemetry-operator/pkg/sidecar"
Expand Down Expand Up @@ -74,8 +76,10 @@ func init() {
func main() {
// registers any flags that underlying libraries might use
opts := zap.Options{}
flagset := flags.Flags(featuregate.GlobalRegistry())
opts.BindFlags(flag.CommandLine)
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.CommandLine.AddGoFlagSet(flagset)

v := version.Get()

Expand Down
32 changes: 32 additions & 0 deletions pkg/flags/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright The OpenTelemetry 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 flags

import (
"flag"

"go.opentelemetry.io/collector/featuregate"
)

const (
featureGatesFlag = "feature-gates"
)

func Flags(reg *featuregate.Registry) *flag.FlagSet {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same comments as in the original PR #1557

The package name should probably be changed or include all operator flags. There are as well missing docs.

How do consumers know which feature flags are supported by the operator - take a look at #1557 (comment)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavolloffay It should be possible to include the list of registered featuregates during startup, I can add that.

Also, how to you feel about the addition of the flag/featuregate package? Should we handle the feature gate flags like we do the rest of the flags in main.go?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have a package for it, but the current flags name is too generic and therefore I would expect it handles all operator flags.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have confirmed that the latest featuregate package includes the registered gates as default values:

func main() {
	colfeaturegate.GlobalRegistry().MustRegister("basic", colfeaturegate.StageAlpha)
	colfeaturegate.GlobalRegistry().MustRegister("advanced", colfeaturegate.StageBeta)
	colfeaturegate.GlobalRegistry().MustRegister("false", colfeaturegate.StageStable, colfeaturegate.WithRegisterRemovalVersion("v0.0.1"))

	// registers any flags that underlying libraries might use
	opts := zap.Options{}
	flagset := featuregate.Flags(colfeaturegate.GlobalRegistry())
	opts.BindFlags(flag.CommandLine)
	pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
	pflag.CommandLine.AddGoFlagSet(flagset)

        ... 

}
❯ go run ./main.go --help 
Usage of /var/folders/kv/z8h_0xts4h7dcr7fl3jsfr540000gp/T/go-build2330073747/b001/exe/main:
      --auto-instrumentation-dotnet-image string   The default OpenTelemetry DotNet instrumentation image. This image is used when no image is specified in the CustomResource. (default "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-dotnet:0.0.0")
      --auto-instrumentation-java-image string     The default OpenTelemetry Java instrumentation image. This image is used when no image is specified in the CustomResource. (default "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java:0.0.0")
      --auto-instrumentation-nodejs-image string   The default OpenTelemetry NodeJS instrumentation image. This image is used when no image is specified in the CustomResource. (default "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-nodejs:0.0.0")
      --auto-instrumentation-python-image string   The default OpenTelemetry Python instrumentation image. This image is used when no image is specified in the CustomResource. (default "ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:0.0.0")
      --collector-image string                     The default OpenTelemetry collector image. This image is used when no image is specified in the CustomResource. (default "ghcr.io/open-telemetry/opentelemetry-collector-releases/opentelemetry-collector:0.0.0")
      --enable-leader-election                     Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.
      --feature-gates flag                         Comma-delimited list of feature gate identifiers. Prefix with '-' to disable the feature. '+' or no prefix will enable the feature. (default advanced,-basic,false)
      --health-probe-addr string                   The address the probe endpoint binds to. (default ":8081")
      --kubeconfig string                          Paths to a kubeconfig. Only required if out-of-cluster.
      --labels stringArray                         Labels to filter away from propagating onto deploys
      --metrics-addr string                        The address the metric endpoint binds to. (default ":8080")
      --operator-opamp-bridge-image string         The default OpenTelemetry Operator OpAMP Bridge image. This image is used when no image is specified in the CustomResource. (default "ghcr.io/open-telemetry/opentelemetry-operator/operator-opamp-bridge:0.0.0")
      --target-allocator-image string              The default OpenTelemetry target allocator image. This image is used when no image is specified in the CustomResource. (default "ghcr.io/open-telemetry/opentelemetry-operator/target-allocator:0.0.0")
      --tls-cipher-suites strings                  Comma-separated list of cipher suites for the server. Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants). If omitted, the default Go cipher suites will be used
      --tls-min-version string                     Minimum TLS version supported. Value must match version names from https://golang.org/pkg/crypto/tls/#pkg-constants. (default "VersionTLS12")
      --webhook-port int                           The port the webhook endpoint binds to. (default 9443)
      --zap-devel                                  Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error)
      --zap-encoder encoder                        Zap log encoding (one of 'json' or 'console')
      --zap-log-level level                        Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', or any integer value > 0 which corresponds to custom debug levels of increasing verbosity
      --zap-stacktrace-level level                 Zap Level at and above which stacktraces are captured (one of 'info', 'error', 'panic').
      --zap-time-encoding time-encoding            Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'). Defaults to 'epoch'.
pflag: help requested
exit status 2

flagSet := new(flag.FlagSet)
flagSet.Var(featuregate.NewFlag(reg), featureGatesFlag,
"Comma-delimited list of feature gate identifiers. Prefix with '-' to disable the feature. '+' or no prefix will enable the feature.")
return flagSet
}
89 changes: 89 additions & 0 deletions pkg/flags/flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright The OpenTelemetry 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 flags

import (
"testing"

"go.opentelemetry.io/collector/featuregate"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
basicGate = "basic"
advancedGate = "advanced"
falseGate = "false"
)

func TestSetFlag(t *testing.T) {
featuregate.GlobalRegistry().MustRegister(basicGate, featuregate.StageAlpha)
featuregate.GlobalRegistry().MustRegister(advancedGate, featuregate.StageBeta)
featuregate.GlobalRegistry().MustRegister(falseGate, featuregate.StageStable, featuregate.WithRegisterRemovalVersion("v0.0.1"))
tests := []struct {
name string
args []string
expectedTrue []string
expectedFalse []string
expectedErr string
}{
{
name: "simple set",
args: []string{"--feature-gates=basic"},
expectedTrue: []string{basicGate},
},
{
name: "two flags",
args: []string{"--feature-gates=basic,advanced"},
expectedTrue: []string{basicGate, advancedGate},
},
{
name: "one true one false",
args: []string{"--feature-gates=basic,-advanced"},
expectedTrue: []string{basicGate},
expectedFalse: []string{advancedGate},
},
{
name: "invalid set",
args: []string{"--feature-gates=01"},
expectedErr: `no such feature gate -01`,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
flgs := Flags(featuregate.GlobalRegistry())
err := flgs.Parse(tt.args)
if tt.expectedErr != "" {
require.Error(t, err)
} else {
require.NoError(t, err)
}
featuregate.GlobalRegistry().VisitAll(func(gate *featuregate.Gate) {
for _, id := range tt.expectedTrue {
if gate.ID() == id {
assert.True(t, gate.IsEnabled())
}
}
for _, id := range tt.expectedFalse {
if gate.ID() == id {
assert.False(t, gate.IsEnabled())
}
}
})
})
}
}