Merge pull request #6185 from bacongobbler/feature-gates

feat(cmd): add feature gates
pull/6193/head
Matthew Fisher 5 years ago committed by GitHub
commit c5137c5390
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

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

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

@ -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 { func newChartPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
return &cobra.Command{ return &cobra.Command{
Use: "pull [ref]", Use: "pull [ref]",
Short: "pull a chart from remote", Short: "pull a chart from remote",
Long: chartPullDesc, Long: chartPullDesc,
Args: require.MinimumNArgs(1), Args: require.MinimumNArgs(1),
Hidden: !FeatureGateOCI.IsEnabled(),
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
ref := args[0] ref := args[0]
return action.NewChartPull(cfg).Run(out, ref) return action.NewChartPull(cfg).Run(out, ref)

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

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

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

@ -24,6 +24,7 @@ import (
"strings" "strings"
"sync" "sync"
"github.com/spf13/cobra"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/klog" "k8s.io/klog"
@ -33,11 +34,15 @@ import (
"helm.sh/helm/pkg/action" "helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/cli" "helm.sh/helm/pkg/cli"
"helm.sh/helm/pkg/gates"
"helm.sh/helm/pkg/kube" "helm.sh/helm/pkg/kube"
"helm.sh/helm/pkg/storage" "helm.sh/helm/pkg/storage"
"helm.sh/helm/pkg/storage/driver" "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 ( var (
settings cli.EnvSettings settings cli.EnvSettings
config genericclioptions.RESTClientGetter config genericclioptions.RESTClientGetter
@ -134,3 +139,12 @@ func getNamespace() string {
func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName { func wordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-")) 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
}
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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

@ -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())
}

@ -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())
}
}
Loading…
Cancel
Save