diff --git a/cmd/stop.go b/cmd/stop.go index eaba91fd..89dc62cd 100644 --- a/cmd/stop.go +++ b/cmd/stop.go @@ -11,11 +11,17 @@ import ( ) func NewStopCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command { + var name string var stopCmd = &cobra.Command{ Use: "stop", Short: "stop microcks instance", Long: "stop microcks instance", + Example: `# Stop the instance from the current context +microcks stop + +# Stop by context name or instance name +microcks stop --name myinstance`, Run: func(cmd *cobra.Command, args []string) { configFile := globalClientOpts.ConfigPath @@ -27,7 +33,12 @@ func NewStopCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command { return } - ctx, err := localConfig.ResolveContext("") + var ctx *config.Context + if name != "" { + ctx, err = resolveStopTarget(name, localConfig) + } else { + ctx, err = localConfig.ResolveContext("") + } errors.CheckError(err) instance := ctx.Instance @@ -73,5 +84,24 @@ func NewStopCommand(globalClientOpts *connectors.ClientOptions) *cobra.Command { }, } + stopCmd.Flags().StringVar(&name, "name", "", "Name of the context or instance to stop (uses current context if empty)") + return stopCmd } + +// resolveStopTarget resolves a stop target by name. +// Context name is checked first because it is the explicit user-assigned +// identifier. Instance name is a secondary label (from start --name) — +// used as fallback when no context with the given name exists. +func resolveStopTarget(name string, cfg *config.LocalConfig) (*config.Context, error) { + ctx, err := cfg.ResolveContext(name) + if err == nil { + return ctx, nil + } + for i := range cfg.Contexts { + if cfg.Contexts[i].Instance == name { + return cfg.ResolveContext(cfg.Contexts[i].Name) + } + } + return nil, fmt.Errorf("no context found for '%s'", name) +} diff --git a/cmd/stop_test.go b/cmd/stop_test.go new file mode 100644 index 00000000..9d0ab3e6 --- /dev/null +++ b/cmd/stop_test.go @@ -0,0 +1,120 @@ +/* + * Copyright The Microcks 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 cmd + +import ( + "testing" + + "github.com/microcks/microcks-cli/pkg/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestResolveStopTargetByContextName(t *testing.T) { + cfg := &config.LocalConfig{ + Contexts: []config.ContextRef{ + {Name: "dev", Server: "http://dev-server:8080", User: "u1"}, + }, + Servers: []config.Server{ + {Server: "http://dev-server:8080"}, + }, + Users: []config.User{ + {Name: "u1"}, + }, + } + + ctx, err := resolveStopTarget("dev", cfg) + require.NoError(t, err) + assert.Equal(t, "dev", ctx.Name) +} + +func TestResolveStopTargetByInstanceName(t *testing.T) { + cfg := &config.LocalConfig{ + Contexts: []config.ContextRef{ + {Name: "http://server", Server: "http://instance-server:8080", User: "u1", Instance: "myinst"}, + }, + Servers: []config.Server{ + {Server: "http://instance-server:8080"}, + }, + Users: []config.User{ + {Name: "u1"}, + }, + } + + ctx, err := resolveStopTarget("myinst", cfg) + require.NoError(t, err) + assert.Equal(t, "http://server", ctx.Name) +} + +func TestResolveStopTargetNotFound(t *testing.T) { + cfg := &config.LocalConfig{ + Contexts: []config.ContextRef{ + {Name: "dev", Server: "http://dev-server:8080", User: "u1"}, + }, + Servers: []config.Server{ + {Server: "http://dev-server:8080"}, + }, + Users: []config.User{ + {Name: "u1"}, + }, + } + + _, err := resolveStopTarget("nonexistent", cfg) + require.Error(t, err) + assert.Contains(t, err.Error(), "nonexistent") +} + +func TestResolveStopTargetCollisionContextWins(t *testing.T) { + cfg := &config.LocalConfig{ + Contexts: []config.ContextRef{ + {Name: "shared", Server: "http://server-a:8080", User: "u1", Instance: "other"}, + {Name: "different", Server: "http://server-b:8080", User: "u2", Instance: "shared"}, + }, + Servers: []config.Server{ + {Server: "http://server-a:8080"}, + {Server: "http://server-b:8080"}, + }, + Users: []config.User{ + {Name: "u1"}, + {Name: "u2"}, + }, + } + + ctx, err := resolveStopTarget("shared", cfg) + require.NoError(t, err) + assert.Equal(t, "shared", ctx.Name) + assert.Equal(t, "http://server-a:8080", ctx.Server.Server) +} + +func TestResolveStopTargetEmptyName(t *testing.T) { + cfg := &config.LocalConfig{ + CurrentContext: "dev", + Contexts: []config.ContextRef{ + {Name: "dev", Server: "http://dev-server:8080", User: "u1"}, + }, + Servers: []config.Server{ + {Server: "http://dev-server:8080"}, + }, + Users: []config.User{ + {Name: "u1"}, + }, + } + + ctx, err := resolveStopTarget("", cfg) + require.NoError(t, err) + assert.Equal(t, "dev", ctx.Name) +}