Skip to content

Commit

Permalink
add enable-feature command
Browse files Browse the repository at this point in the history
  • Loading branch information
MatousJobanek committed Jun 28, 2024
1 parent ffc4214 commit dcac8f3
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 3 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/kubesaw/ksctl
go 1.20

require (
github.com/codeready-toolchain/api v0.0.0-20240530120602-c11598ccffb7
github.com/codeready-toolchain/api v0.0.0-20240627084210-f4a765461e75
github.com/codeready-toolchain/toolchain-common v0.0.0-20240530121312-98aad712838f
github.com/ghodss/yaml v1.0.0
github.com/mitchellh/go-homedir v1.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:z
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
github.com/codeready-toolchain/api v0.0.0-20240530120602-c11598ccffb7 h1:o5JLcHCVS1BlZevw2mh1mH+iKwn9fLUrT1Ek8NFjvPY=
github.com/codeready-toolchain/api v0.0.0-20240530120602-c11598ccffb7/go.mod h1:ie9p4LenCCS0LsnbWp6/xwpFDdCWYE0KWzUO6Sk1g0E=
github.com/codeready-toolchain/api v0.0.0-20240627084210-f4a765461e75 h1:LOrOMTw3bplM+VwzSxi3j9GNseujhcebO/LGr8b95GE=
github.com/codeready-toolchain/api v0.0.0-20240627084210-f4a765461e75/go.mod h1:ie9p4LenCCS0LsnbWp6/xwpFDdCWYE0KWzUO6Sk1g0E=
github.com/codeready-toolchain/toolchain-common v0.0.0-20240530121312-98aad712838f h1:2qfRfyh7wfEnnfxrUtQeQrvhzWlkBCN0B/UXv1YUMiA=
github.com/codeready-toolchain/toolchain-common v0.0.0-20240530121312-98aad712838f/go.mod h1:cyHrUfvBYEtsf+FbqQYmR9y0AQi9QAVtM3SUWLA5bd4=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
Expand Down
62 changes: 62 additions & 0 deletions pkg/cmd/enable-feature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package cmd

import (
"strings"

toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
"github.com/kubesaw/ksctl/pkg/client"
clicontext "github.com/kubesaw/ksctl/pkg/context"
"github.com/kubesaw/ksctl/pkg/ioutils"
"k8s.io/utils/strings/slices"

"github.com/spf13/cobra"
)

func NewEnableFeatureCmd() *cobra.Command {
return &cobra.Command{
Use: "enable-feature <space-name> <feature-name>",
Short: "Enable a feature for the given Space",
Long: `Enable a feature toggle for the given Space. There are two expected
parameters - the first one is the Space name and the second is the name of the feature toggle that should be enabled for the Space.`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
term := ioutils.NewTerminal(cmd.InOrStdin, cmd.OutOrStdout)
ctx := clicontext.NewCommandContext(term, client.DefaultNewClient)
return EnableFeature(ctx, args[0], args[1])
},

Check warning on line 26 in pkg/cmd/enable-feature.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/enable-feature.go#L23-L26

Added lines #L23 - L26 were not covered by tests
}
}

func EnableFeature(ctx *clicontext.CommandContext, spaceName, featureToggleName string) error {
return client.PatchSpace(ctx, spaceName, func(space *toolchainv1alpha1.Space) (bool, error) {
currentFeatures := strings.TrimSpace(space.Annotations[toolchainv1alpha1.FeatureToggleNameAnnotationKey])
var enabledFeatures []string
if currentFeatures != "" {
enabledFeatures = strings.Split(currentFeatures, ",")
}
if err := ctx.PrintObject(space, "The current Space"); err != nil {
return false, err

Check warning on line 38 in pkg/cmd/enable-feature.go

View check run for this annotation

Codecov / codecov/patch

pkg/cmd/enable-feature.go#L38

Added line #L38 was not covered by tests
}

if slices.Contains(enabledFeatures, featureToggleName) {
ctx.Println("")
ctx.Println("The space has the feature toggle already enabled. There is nothing to do.")
ctx.Println("")
return false, nil
}

confirmation := ctx.AskForConfirmation(ioutils.WithMessagef(
"enable the feature toggle '%s' for the Space '%s'? The already enabled feature toggles are '%s'.",
featureToggleName, spaceName, currentFeatures))

if confirmation {
enabledFeatures = append(enabledFeatures, featureToggleName)
if space.Annotations == nil {
space.Annotations = map[string]string{}
}
space.Annotations[toolchainv1alpha1.FeatureToggleNameAnnotationKey] = strings.Join(enabledFeatures, ",")
return true, nil
}
return false, nil
}, "Successfully enabled feature toggle for the Space")
}
151 changes: 151 additions & 0 deletions pkg/cmd/enable_feature_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package cmd_test

import (
"context"
"fmt"
"testing"

toolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1"
"github.com/codeready-toolchain/toolchain-common/pkg/test"
"github.com/kubesaw/ksctl/pkg/cmd"
clicontext "github.com/kubesaw/ksctl/pkg/context"
. "github.com/kubesaw/ksctl/pkg/test"

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

func TestEnableFeatureCmd(t *testing.T) {
// given
SetFileConfig(t, Host())

var combinations = []struct {
alreadyEnabled string
afterEnable map[string]string
}{
{
alreadyEnabled: "",
afterEnable: map[string]string{
toolchainv1alpha1.FeatureToggleNameAnnotationKey: "feature-x",
},
},
{
alreadyEnabled: "feature0",
afterEnable: map[string]string{
toolchainv1alpha1.FeatureToggleNameAnnotationKey: "feature0,feature-x",
},
},
{
alreadyEnabled: "feature1,feature2,feature3",
afterEnable: map[string]string{
toolchainv1alpha1.FeatureToggleNameAnnotationKey: "feature1,feature2,feature3,feature-x",
},
},
}

for _, data := range combinations {
t.Run("with the already enabled features: "+data.alreadyEnabled, func(t *testing.T) {
// given
space := newSpace()
if data.alreadyEnabled != "" {
space.Annotations = map[string]string{
toolchainv1alpha1.FeatureToggleNameAnnotationKey: data.alreadyEnabled,
}
}

for _, answer := range []string{"Y", "n"} {

t.Run("when answer is "+answer, func(t *testing.T) {
// given
newClient, fakeClient := NewFakeClients(t, space)
term := NewFakeTerminalWithResponse(answer)
ctx := clicontext.NewCommandContext(term, newClient)

// when
err := cmd.EnableFeature(ctx, space.Name, "feature-x")

// then
require.NoError(t, err)

output := term.Output()
assert.Contains(t, output, fmt.Sprintf("enable the feature toggle 'feature-x' for the Space 'testspace'? The already enabled feature toggles are '%s'.", data.alreadyEnabled))
assert.NotContains(t, output, "cool-token")
expectedSpace := newSpace()

if answer == "Y" {
expectedSpace.Annotations = data.afterEnable
assert.Contains(t, output, "Successfully enabled feature toggle for the Space")

} else {
expectedSpace.Annotations = space.Annotations
assert.NotContains(t, output, "Successfully enabled feature toggle for the Space")
}
assertSpaceAnnotations(t, fakeClient, expectedSpace)

})
}
})
}
}

func TestEnableFeatureCmdWhenFeatureIsAlreadyEnabled(t *testing.T) {
// given
SetFileConfig(t, Host())

for _, alreadyEnabled := range []string{"feature-x", "feature-x,feature0", "feature1,feature2,feature-x,feature3"} {
t.Run("with the already enabled features: "+alreadyEnabled, func(t *testing.T) {
// given
space := newSpace()
if alreadyEnabled != "" {
space.Annotations = map[string]string{
toolchainv1alpha1.FeatureToggleNameAnnotationKey: alreadyEnabled,
}
}
// given
newClient, fakeClient := NewFakeClients(t, space)
term := NewFakeTerminalWithResponse("Y")
ctx := clicontext.NewCommandContext(term, newClient)

// when
err := cmd.EnableFeature(ctx, space.Name, "feature-x")

// then
require.NoError(t, err)
assertSpaceAnnotations(t, fakeClient, space) // no change

output := term.Output()
assert.Contains(t, output, "The space has the feature toggle already enabled. There is nothing to do.")
assert.NotContains(t, output, "enable the feature toggle 'feature-x' for the Space 'testspace'?")
assert.NotContains(t, output, "Successfully enabled feature toggle for the Space")
assert.NotContains(t, output, "cool-token")

})
}
}

func TestEnableFeatureCmdWhenSpaceNotFound(t *testing.T) {
// given
space := newSpace()
newClient, fakeClient := NewFakeClients(t, space)
SetFileConfig(t, Host())
term := NewFakeTerminalWithResponse("Y")
ctx := clicontext.NewCommandContext(term, newClient)

// when
err := cmd.EnableFeature(ctx, "another", "feature-x")

// then
require.EqualError(t, err, "spaces.toolchain.dev.openshift.com \"another\" not found")
assertSpaceAnnotations(t, fakeClient, space) // unrelated space should be unchanged
output := term.Output()
assert.NotContains(t, output, "enable the feature toggle 'feature-x' for the Space 'testspace'?")
assert.NotContains(t, output, "Successfully enabled feature toggle for the Space")
assert.NotContains(t, output, "cool-token")
}

func assertSpaceAnnotations(t *testing.T, fakeClient *test.FakeClient, expectedSpace *toolchainv1alpha1.Space) {
updatedSpace := &toolchainv1alpha1.Space{}
err := fakeClient.Get(context.TODO(), test.NamespacedName(expectedSpace.Namespace, expectedSpace.Name), updatedSpace)
require.NoError(t, err)
assert.Equal(t, expectedSpace.Annotations, updatedSpace.Annotations)
}
1 change: 1 addition & 0 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func init() {
rootCmd.AddCommand(NewLogsCmd())
rootCmd.AddCommand(NewDescribeCmd())
rootCmd.AddCommand(NewDisableUserCmd())
rootCmd.AddCommand(NewEnableFeatureCmd())

// administrative commands
rootCmd.AddCommand(adm.NewAdmCmd())
Expand Down

0 comments on commit dcac8f3

Please sign in to comment.