Merge pull request #6185 from bacongobbler/feature-gates

feat(cmd): add feature gates
This commit is contained in:
Matthew Fisher
2019-08-08 21:58:39 -07:00
committed by GitHub
32 changed files with 176 additions and 45 deletions

View File

@@ -33,9 +33,11 @@ Example usage:
func newChartCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "chart",
Short: "push, pull, tag, or remove Helm charts",
Long: chartHelp,
Use: "chart",
Short: "push, pull, tag, or remove Helm charts",
Long: chartHelp,
Hidden: !FeatureGateOCI.IsEnabled(),
PersistentPreRunE: checkOCIFeatureGate(),
}
cmd.AddCommand(
newChartListCmd(cfg, out),

View File

@@ -35,10 +35,11 @@ and check into source control if desired.
func newChartExportCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "export [ref]",
Short: "export a chart to directory",
Long: chartExportDesc,
Args: require.MinimumNArgs(1),
Use: "export [ref]",
Short: "export a chart to directory",
Long: chartExportDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartExport(cfg).Run(out, ref)

View File

@@ -36,6 +36,7 @@ func newChartListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Aliases: []string{"ls"},
Short: "list all saved charts",
Long: chartListDesc,
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
return action.NewChartList(cfg).Run(out)
},

View File

@@ -33,10 +33,11 @@ This will store the chart in the local registry cache to be used later.
func newChartPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "pull [ref]",
Short: "pull a chart from remote",
Long: chartPullDesc,
Args: require.MinimumNArgs(1),
Use: "pull [ref]",
Short: "pull a chart from remote",
Long: chartPullDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartPull(cfg).Run(out, ref)

View File

@@ -35,10 +35,11 @@ Must first run "helm chart save" or "helm chart pull".
func newChartPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "push [ref]",
Short: "push a chart to remote",
Long: chartPushDesc,
Args: require.MinimumNArgs(1),
Use: "push [ref]",
Short: "push a chart to remote",
Long: chartPushDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartPush(cfg).Run(out, ref)

View File

@@ -41,6 +41,7 @@ func newChartRemoveCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
Short: "remove a chart",
Long: chartRemoveDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0]
return action.NewChartRemove(cfg).Run(out, ref)

View File

@@ -36,10 +36,11 @@ not change the item as it exists in the cache.
func newChartSaveCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "save [path] [ref]",
Short: "save a chart directory",
Long: chartSaveDesc,
Args: require.MinimumNArgs(2),
Use: "save [path] [ref]",
Short: "save a chart directory",
Long: chartSaveDesc,
Args: require.MinimumNArgs(2),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
path := args[0]
ref := args[1]

View File

@@ -24,6 +24,7 @@ import (
"strings"
"sync"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/klog"
@@ -33,11 +34,15 @@ import (
"helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/gates"
"helm.sh/helm/pkg/kube"
"helm.sh/helm/pkg/storage"
"helm.sh/helm/pkg/storage/driver"
)
// FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work
const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI")
var (
settings cli.EnvSettings
config genericclioptions.RESTClientGetter
@@ -134,3 +139,12 @@ func getNamespace() string {
func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-"))
}
func checkOCIFeatureGate() func(_ *cobra.Command, _ []string) error {
return func(_ *cobra.Command, _ []string) error {
if !FeatureGateOCI.IsEnabled() {
return FeatureGateOCI.Error()
}
return nil
}
}

View File

@@ -33,9 +33,11 @@ Example usage:
func newRegistryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "registry",
Short: "login to or logout from a registry",
Long: registryHelp,
Use: "registry",
Short: "login to or logout from a registry",
Long: registryHelp,
Hidden: !FeatureGateOCI.IsEnabled(),
PersistentPreRunE: checkOCIFeatureGate(),
}
cmd.AddCommand(
newRegistryLoginCmd(cfg, out),

View File

@@ -41,10 +41,11 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
var passwordFromStdinOpt bool
cmd := &cobra.Command{
Use: "login [host]",
Short: "login to a registry",
Long: registryLoginDesc,
Args: require.MinimumNArgs(1),
Use: "login [host]",
Short: "login to a registry",
Long: registryLoginDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
hostname := args[0]

View File

@@ -31,10 +31,11 @@ Remove credentials stored for a remote registry.
func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{
Use: "logout [host]",
Short: "logout from a registry",
Long: registryLogoutDesc,
Args: require.MinimumNArgs(1),
Use: "logout [host]",
Short: "logout from a registry",
Long: registryLogoutDesc,
Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error {
hostname := args[0]
return action.NewRegistryLogout(cfg).Run(out, hostname)

View File

@@ -25,9 +25,9 @@ import (
"github.com/spf13/cobra"
"helm.sh/helm/cmd/helm/require"
"helm.sh/helm/internal/experimental/registry"
"helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/helmpath"
"helm.sh/helm/pkg/registry"
)
const (

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package registry // import "helm.sh/helm/pkg/registry"
package registry // import "helm.sh/helm/internal/experimental/registry"
import (
"github.com/deislabs/oras/pkg/auth"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package registry // import "helm.sh/helm/pkg/registry"
package registry // import "helm.sh/helm/internal/experimental/registry"
import (
"bytes"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package registry // import "helm.sh/helm/pkg/registry"
package registry // import "helm.sh/helm/internal/experimental/registry"
import (
"context"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package registry // import "helm.sh/helm/pkg/registry"
package registry // import "helm.sh/helm/internal/experimental/registry"
const (
// HelmChartConfigMediaType is the reserved media type for the Helm chart manifest config

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package registry // import "helm.sh/helm/pkg/registry"
package registry // import "helm.sh/helm/internal/experimental/registry"
import (
"errors"

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package registry // import "helm.sh/helm/pkg/registry"
package registry // import "helm.sh/helm/internal/experimental/registry"
import (
"github.com/containerd/containerd/remotes"

View File

@@ -30,10 +30,10 @@ import (
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"helm.sh/helm/internal/experimental/registry"
"helm.sh/helm/pkg/chartutil"
"helm.sh/helm/pkg/hooks"
"helm.sh/helm/pkg/kube"
"helm.sh/helm/pkg/registry"
"helm.sh/helm/pkg/release"
"helm.sh/helm/pkg/storage"
)

View File

@@ -25,10 +25,10 @@ import (
dockerauth "github.com/deislabs/oras/pkg/auth/docker"
fakeclientset "k8s.io/client-go/kubernetes/fake"
"helm.sh/helm/internal/experimental/registry"
"helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/chartutil"
kubefake "helm.sh/helm/pkg/kube/fake"
"helm.sh/helm/pkg/registry"
"helm.sh/helm/pkg/release"
"helm.sh/helm/pkg/storage"
"helm.sh/helm/pkg/storage/driver"

View File

@@ -20,8 +20,8 @@ import (
"fmt"
"io"
"helm.sh/helm/internal/experimental/registry"
"helm.sh/helm/pkg/chartutil"
"helm.sh/helm/pkg/registry"
)
// ChartExport performs a chart export operation.

View File

@@ -19,7 +19,7 @@ package action
import (
"io"
"helm.sh/helm/pkg/registry"
"helm.sh/helm/internal/experimental/registry"
)
// ChartPull performs a chart pull operation.

View File

@@ -19,7 +19,7 @@ package action
import (
"io"
"helm.sh/helm/pkg/registry"
"helm.sh/helm/internal/experimental/registry"
)
// ChartPush performs a chart push operation.

View File

@@ -19,7 +19,7 @@ package action
import (
"io"
"helm.sh/helm/pkg/registry"
"helm.sh/helm/internal/experimental/registry"
)
// ChartRemove performs a chart remove operation.

View File

@@ -19,8 +19,8 @@ package action
import (
"io"
"helm.sh/helm/internal/experimental/registry"
"helm.sh/helm/pkg/chart"
"helm.sh/helm/pkg/registry"
)
// ChartSave performs a chart save operation.

View File

@@ -20,7 +20,7 @@ import (
"io/ioutil"
"testing"
"helm.sh/helm/pkg/registry"
"helm.sh/helm/internal/experimental/registry"
)
func chartSaveAction(t *testing.T) *ChartSave {

20
pkg/gates/doc.go Normal file
View File

@@ -0,0 +1,20 @@
/*
Copyright The Helm 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 gates provides a general tool for working with experimental feature gates.
This provides convenience methods where the user can determine if certain experimental features are enabled.
*/
package gates

38
pkg/gates/gates.go Normal file
View File

@@ -0,0 +1,38 @@
/*
Copyright The Helm 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 gates
import (
"fmt"
"os"
)
// Gate is the name of the feature gate.
type Gate string
// String returns the string representation of this feature gate.
func (g Gate) String() string {
return string(g)
}
// IsEnabled determines whether a certain feature gate is enabled.
func (g Gate) IsEnabled() bool {
return os.Getenv(string(g)) != ""
}
func (g Gate) Error() error {
return fmt.Errorf("this feature has been marked as experimental and is not enabled by default. Please set $%s in your environment to use this feature", g.String())
}

47
pkg/gates/gates_test.go Normal file
View File

@@ -0,0 +1,47 @@
/*
Copyright The Helm 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 gates
import (
"os"
"testing"
)
const name string = "HELM_EXPERIMENTAL_FEATURE"
func TestIsEnabled(t *testing.T) {
os.Unsetenv(name)
g := Gate(name)
if g.IsEnabled() {
t.Errorf("feature gate shows as available, but the environment variable $%s was not set", name)
}
os.Setenv(name, "1")
if !g.IsEnabled() {
t.Errorf("feature gate shows as disabled, but the environment variable $%s was set", name)
}
}
func TestError(t *testing.T) {
os.Unsetenv(name)
g := Gate(name)
if g.Error().Error() != "this feature has been marked as experimental and is not enabled by default. Please set $HELM_EXPERIMENTAL_FEATURE in your environment to use this feature" {
t.Errorf("incorrect error message. Received %s", g.Error().Error())
}
}