Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
37 changes: 34 additions & 3 deletions pkg/cli/cmd/env/create/preview/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package preview

import (
"context"
"fmt"

"github.com/spf13/cobra"

Expand All @@ -27,6 +28,7 @@ import (
"github.com/radius-project/radius/pkg/cli/clierrors"
"github.com/radius-project/radius/pkg/cli/cmd"
"github.com/radius-project/radius/pkg/cli/cmd/commonflags"
"github.com/radius-project/radius/pkg/cli/cmd/group/common"
"github.com/radius-project/radius/pkg/cli/connections"
"github.com/radius-project/radius/pkg/cli/framework"
"github.com/radius-project/radius/pkg/cli/output"
Expand Down Expand Up @@ -70,6 +72,9 @@ rad env create myenv --kubernetes-namespace mynamespace

## Create environment with recipe packs (--preview)
rad env create myenv --preview --recipe-packs pack1,pack2

## Create environment with recipe packs from a different resource group (--preview)
rad env create myenv --preview --recipe-packs pack1 --recipe-pack-group other-group
`,
RunE: framework.RunCommand(runner),
}
Expand All @@ -88,6 +93,7 @@ rad env create myenv --preview --recipe-packs pack1,pack2
commonflags.MarkNamespaceFlagDeprecated(cmd)
cmd.MarkFlagsMutuallyExclusive(commonflags.KubernetesNamespaceFlag, commonflags.NamespaceFlag)
cmd.Flags().StringSliceP("recipe-packs", "", []string{}, "Specify recipe packs to assign to the environment (--preview). Accepts comma-separated values.")
cmd.Flags().StringP("recipe-pack-group", "", "", "Specify the resource group containing the recipe packs named in --recipe-packs, if different from the environment's resource group (--preview).")

return cmd, runner
}
Expand All @@ -107,8 +113,9 @@ type Runner struct {
ConfigFileInterface framework.ConfigFileInterface
ConnectionFactory connections.Factory

recipePacks []string
providers *corerpv20250801.Providers
recipePacks []string
recipePackGroup string
providers *corerpv20250801.Providers
}

// NewRunner creates a new instance of the `rad env create` runner.
Expand Down Expand Up @@ -237,6 +244,21 @@ func (r *Runner) Validate(cmd *cobra.Command, args []string) error {
return clierrors.Message("No valid recipe packs were provided. Specify one or more recipe pack names or IDs with --recipe-packs.")
}

r.recipePackGroup, err = cmd.Flags().GetString("recipe-pack-group")
if err != nil {
return err
}

if r.recipePackGroup != "" && !cmd.Flags().Changed("recipe-packs") {
return clierrors.Message("--recipe-pack-group can only be used together with --recipe-packs.")
Comment thread
lakshmimsft marked this conversation as resolved.
}

if r.recipePackGroup != "" {
if err := common.ValidateResourceGroupName(r.recipePackGroup); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -320,9 +342,18 @@ func (r *Runner) resolveRecipePacks(ctx context.Context) ([]*string, error) {

recipePackClient := r.RadiusCoreClientFactory.NewRecipePacksClient()

recipePackScope := r.Workspace.Scope
if r.recipePackGroup != "" {
workspaceScopeID, err := resources.ParseScope(r.Workspace.Scope)
if err != nil {
return nil, err
}
recipePackScope = fmt.Sprintf("%s/resourceGroups/%s", workspaceScopeID.PlaneScope(), r.recipePackGroup)
}

recipePackIDs := make([]*string, 0, len(r.recipePacks))
for _, recipePack := range r.recipePacks {
recipePackID, isFullID, err := recipepack.ResolveID(recipePack, r.Workspace.Scope)
recipePackID, isFullID, err := recipepack.ResolveID(recipePack, recipePackScope)
if err != nil {
return nil, err
}
Expand Down
81 changes: 81 additions & 0 deletions pkg/cli/cmd/env/create/preview/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,43 @@ func Test_Validate(t *testing.T) {
expectResourceGroupSuccess(mocks.ApplicationManagementClient, "test-resource-group")
},
},
{
Name: "Create command with --recipe-pack-group flag",
Input: []string{"testingenv", "--recipe-packs", "pack1", "--recipe-pack-group", "other-group"},
ExpectedValid: true,
ConfigHolder: framework.ConfigHolder{
Config: configWithWorkspace,
},
ConfigureMocks: func(mocks radcli.ValidateMocks) {
expectResourceGroupSuccess(mocks.ApplicationManagementClient, "test-resource-group")
},
ValidateCallback: func(t *testing.T, runner framework.Runner) {
r := runner.(*Runner)
require.Equal(t, "other-group", r.recipePackGroup)
},
},
{
Name: "Create command with --recipe-pack-group flag but no --recipe-packs",
Input: []string{"testingenv", "--recipe-pack-group", "other-group"},
ExpectedValid: false,
ConfigHolder: framework.ConfigHolder{
Config: configWithWorkspace,
},
ConfigureMocks: func(mocks radcli.ValidateMocks) {
expectResourceGroupSuccess(mocks.ApplicationManagementClient, "test-resource-group")
},
},
{
Name: "Create command with invalid --recipe-pack-group value",
Input: []string{"testingenv", "--recipe-packs", "pack1", "--recipe-pack-group", "invalid group name!"},
ExpectedValid: false,
ConfigHolder: framework.ConfigHolder{
Config: configWithWorkspace,
},
ConfigureMocks: func(mocks radcli.ValidateMocks) {
expectResourceGroupSuccess(mocks.ApplicationManagementClient, "test-resource-group")
},
},
}
radcli.SharedValidateValidation(t, NewCommand, testcases)
}
Expand Down Expand Up @@ -683,6 +720,50 @@ func Test_Run(t *testing.T) {
require.Contains(t, *referencedByUpdate[0], "Radius.Core/environments/testenv")
})

t.Run("resolves a bare recipe pack name against recipePackGroup", func(t *testing.T) {
var capturedEnv v20250801preview.EnvironmentResource

capturingEnvServer := func() corerpfake.EnvironmentsServer {
return corerpfake.EnvironmentsServer{
CreateOrUpdate: func(
ctx context.Context,
rootScope string,
environmentName string,
resource v20250801preview.EnvironmentResource,
options *v20250801preview.EnvironmentsClientCreateOrUpdateOptions,
) (resp azfake.Responder[v20250801preview.EnvironmentsClientCreateOrUpdateResponse], errResp azfake.ErrorResponder) {
capturedEnv = resource
resp.SetResponse(http.StatusOK, v20250801preview.EnvironmentsClientCreateOrUpdateResponse{EnvironmentResource: resource}, nil)
return
},
}
}

factory, err := test_client_factory.NewRadiusCoreTestClientFactory(
workspace.Scope,
capturingEnvServer,
test_client_factory.WithRecipePackServerNoError,
)
require.NoError(t, err)

runner := &Runner{
RadiusCoreClientFactory: factory,
Output: &output.MockOutput{},
Workspace: workspace,
EnvironmentName: "testenv",
ResourceGroupName: "test-resource-group",
recipePacks: []string{"mypack"},
recipePackGroup: "other-group",
}

err = runner.Run(t.Context())
require.NoError(t, err)

// The bare name is resolved against recipePackGroup, not the workspace's own resource group.
require.Len(t, capturedEnv.Properties.RecipePacks, 1)
require.Equal(t, "/planes/radius/local/resourceGroups/other-group/providers/Radius.Core/recipePacks/mypack", *capturedEnv.Properties.RecipePacks[0])
})

t.Run("returns error when specified recipe pack does not exist", func(t *testing.T) {
factory, err := test_client_factory.NewRadiusCoreTestClientFactory(
workspace.Scope,
Expand Down
39 changes: 38 additions & 1 deletion pkg/cli/cmd/env/update/preview/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package preview

import (
"context"
"fmt"

"github.com/spf13/cobra"

Expand All @@ -26,6 +27,7 @@ import (
"github.com/radius-project/radius/pkg/cli/clierrors"
"github.com/radius-project/radius/pkg/cli/cmd"
"github.com/radius-project/radius/pkg/cli/cmd/commonflags"
"github.com/radius-project/radius/pkg/cli/cmd/group/common"
"github.com/radius-project/radius/pkg/cli/framework"
"github.com/radius-project/radius/pkg/cli/output"
"github.com/radius-project/radius/pkg/cli/recipepack"
Expand Down Expand Up @@ -86,6 +88,9 @@ rad env update myenv --clear-kubernetes

## Set recipe packs to environment (--preview)
rad env update myenv --recipe-packs pack1,pack2

## Set recipe packs from a different resource group to environment (--preview)
rad env update myenv --recipe-packs pack1 --recipe-pack-group other-group
`,
RunE: framework.RunCommand(runner),
}
Expand All @@ -96,6 +101,7 @@ rad env update myenv --recipe-packs pack1,pack2
cmd.Flags().Bool(commonflags.ClearEnvAWSFlag, false, "Specify if aws provider needs to be cleared on env")
cmd.Flags().Bool(commonflags.ClearEnvKubernetesFlag, false, "Specify if kubernetes provider needs to be cleared on env (--preview)")
cmd.Flags().StringSliceP("recipe-packs", "", []string{}, "Specify recipe packs to replace the environment's recipe pack list (--preview). Accepts comma-separated values.")
cmd.Flags().StringP("recipe-pack-group", "", "", "Specify the resource group containing the recipe packs named in --recipe-packs, if different from the environment's resource group (--preview).")
commonflags.AddAzureScopeFlags(cmd)
commonflags.AddAWSScopeFlags(cmd)
commonflags.AddKubernetesScopeFlags(cmd)
Expand All @@ -119,6 +125,7 @@ type Runner struct {
providers *corerpv20250801.Providers
noFlagsSet bool
recipePacks []string
recipePackGroup string
}

// NewRunner creates a new instance of the `rad env update` preview runner.
Expand Down Expand Up @@ -215,6 +222,27 @@ func (r *Runner) Validate(cmd *cobra.Command, args []string) error {

r.recipePacks = recipepack.NormalizeRecipePacks(recipePacks)

// Reject an explicitly provided but effectively empty --recipe-packs value
// (e.g. "," or " ") rather than silently skipping the recipe-pack update.
if cmd.Flags().Changed("recipe-packs") && len(r.recipePacks) == 0 {
return clierrors.Message("No valid recipe packs were provided. Specify one or more recipe pack names or IDs with --recipe-packs.")
}

r.recipePackGroup, err = cmd.Flags().GetString("recipe-pack-group")
if err != nil {
return err
}

if r.recipePackGroup != "" && !cmd.Flags().Changed("recipe-packs") {
return clierrors.Message("--recipe-pack-group can only be used together with --recipe-packs.")
}

if r.recipePackGroup != "" {
if err := common.ValidateResourceGroupName(r.recipePackGroup); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -341,8 +369,17 @@ func (r *Runner) resolveRecipePacks(ctx context.Context) ([]*string, error) {
recipePackClient := r.RadiusCoreClientFactory.NewRecipePacksClient()
recipePackIDs := make([]*string, 0, len(r.recipePacks))

recipePackScope := r.Workspace.Scope
if r.recipePackGroup != "" {
workspaceScopeID, err := resources.ParseScope(r.Workspace.Scope)
if err != nil {
return nil, err
}
recipePackScope = fmt.Sprintf("%s/resourceGroups/%s", workspaceScopeID.PlaneScope(), r.recipePackGroup)
}

for _, recipePack := range r.recipePacks {
recipePackID, isFullID, err := recipepack.ResolveID(recipePack, r.Workspace.Scope)
recipePackID, isFullID, err := recipepack.ResolveID(recipePack, recipePackScope)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading