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
5 changes: 2 additions & 3 deletions docs/guide/command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ covered in [global-flags.md](global-flags.md).
> `queue`/`topic`/`stream rm`) **require `--yes`** when run under `--json` or
> non-interactively.

> **Promotion note.** `shared expose` / `shared ports` are the current spelling;
> a branch is promoting them to top-level `expose` / `ports` (mid-refactor). Use
> the `shared`-prefixed forms today.
> **Naming note.** `expose` / `ports` exist at the top level (canonical) and as
> `shared expose` / `shared ports` (backward-compatible aliases) — same behavior.

## Lifecycle

Expand Down
4 changes: 2 additions & 2 deletions docs/guide/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ devstack shared ports # show the published ports + connection stri
```

See [Shared services & host access](shared-services.md) for the details. (These
two verbs are currently spelled `shared expose` / `shared ports`; a branch is
promoting them to top-level `expose` / `ports`.)
verbs work as top-level `expose` / `ports` and as `shared expose` / `shared ports`
aliases.)

## Stateless CLI, no daemon

Expand Down
9 changes: 4 additions & 5 deletions docs/guide/shared-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,10 @@ Key properties:
- **Opt-in per service** — with no arguments every exposable engine is published;
name engines to narrow it.

> **Naming note.** The current, shipping spelling is `devstack shared expose` and
> `devstack shared ports` (documented here). These are being **promoted to
> top-level** commands — `devstack expose` and `devstack ports` — on a branch
> that is still mid-refactor. When that lands, the top-level forms become the
> canonical spelling; the `shared`-prefixed forms are what you use today.
> **Naming note.** These are available **both** as top-level `devstack expose` /
> `devstack ports` (the canonical spelling) **and** as `devstack shared expose` /
> `devstack shared ports` (backward-compatible aliases). Use whichever you prefer —
> they run the same logic.

## Seeing the ports: `shared ports`

Expand Down
16 changes: 8 additions & 8 deletions internal/cli/expose.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (
"github.com/open-source-cloud/devstack/internal/orchestrate"
)

// newSharedExposeCmd wires `shared expose [services...]` — publish the shared
// engines on stable 127.0.0.1 host ports so GUI clients (DataGrip, a Redis/S3
// browser, the RabbitMQ UI) can connect. Opt-in and loopback-only; it never
// touches the deterministic generated compose (an up-time overlay). `--off`
// removes the publish and returns the stack to DNS-only.
func newSharedExposeCmd(g *GlobalOpts) *cobra.Command {
// newExposeCmd wires the top-level `expose [services...]` (also `shared expose`) —
// publish the shared engines on stable 127.0.0.1 host ports so GUI clients
// (DataGrip, a Redis/S3 browser, the RabbitMQ UI) can connect. Opt-in and
// loopback-only; it never touches the deterministic generated compose (an up-time
// overlay). `--off` removes the publish and returns the stack to DNS-only.
func newExposeCmd(g *GlobalOpts) *cobra.Command {
var off bool
cmd := &cobra.Command{
Use: "expose [services...]",
Expand Down Expand Up @@ -53,9 +53,9 @@ func newSharedExposeCmd(g *GlobalOpts) *cobra.Command {
return cmd
}

// newSharedPortsCmd wires `shared ports` — the read-only projection of the
// newPortsCmd wires `ports` (and `shared ports`) — the read-only projection of the
// currently-published host ports + connection strings (lock-free snapshot).
func newSharedPortsCmd(g *GlobalOpts) *cobra.Command {
func newPortsCmd(g *GlobalOpts) *cobra.Command {
return &cobra.Command{
Use: "ports",
Short: "Show the published 127.0.0.1 host ports for shared services (and connection strings)",
Expand Down
87 changes: 17 additions & 70 deletions internal/cli/expose_test.go
Original file line number Diff line number Diff line change
@@ -1,76 +1,23 @@
package cli

import (
"bytes"
"strings"
"testing"

"github.com/spf13/cobra"

"github.com/open-source-cloud/devstack/internal/orchestrate"
)

func exposeFixture() []orchestrate.ExposedPort {
return []orchestrate.ExposedPort{
{Instance: "postgres", Engine: "postgres", Alias: "shared-postgres", Label: "postgres", Host: "127.0.0.1", Port: 55432, Container: 5432, Primary: true, URL: "postgres://devstack:devstack@127.0.0.1:55432/postgres?sslmode=disable"},
{Instance: "minio", Engine: "minio", Alias: "shared-minio", Label: "console", Host: "127.0.0.1", Port: 59001, Container: 9001, Primary: false, URL: "http://127.0.0.1:59001"},
}
}

func TestRenderExposed_Table(t *testing.T) {
var buf bytes.Buffer
cmd := &cobra.Command{}
cmd.SetOut(&buf)
if err := renderExposed(cmd, &GlobalOpts{}, exposeFixture()); err != nil {
t.Fatal(err)
}
out := buf.String()
for _, want := range []string{"shared-postgres", "55432", "shared-minio (console)", "59001", "per-project database:"} {
if !strings.Contains(out, want) {
t.Errorf("table missing %q:\n%s", want, out)
import "testing"

// TestExposePortsRegistered asserts expose/ports are available both at the top
// level (the promotion) and under `shared` (backward-compatible aliases).
func TestExposePortsRegistered(t *testing.T) {
root := NewRootCmd(Options{})
for _, path := range [][]string{
{"expose"}, {"ports"},
{"shared", "expose"}, {"shared", "ports"},
} {
c, _, err := root.Find(path)
if err != nil || c.RunE == nil {
t.Errorf("%v not registered as a real command: %v", path, err)
}
}
}

func TestRenderExposed_JSON(t *testing.T) {
var buf bytes.Buffer
cmd := &cobra.Command{}
cmd.SetOut(&buf)
if err := renderExposed(cmd, &GlobalOpts{JSON: true}, exposeFixture()); err != nil {
t.Fatal(err)
}
out := buf.String()
if !strings.Contains(out, "\"exposed\"") || !strings.Contains(out, "\"port\": 55432") {
t.Errorf("json missing fields:\n%s", out)
}
}

func TestRenderExposed_Quiet(t *testing.T) {
var buf bytes.Buffer
cmd := &cobra.Command{}
cmd.SetOut(&buf)
if err := renderExposed(cmd, &GlobalOpts{Quiet: true}, exposeFixture()); err != nil {
t.Fatal(err)
}
out := strings.TrimSpace(buf.String())
// Quiet emits only the connection URLs, one per line.
lines := strings.Split(out, "\n")
if len(lines) != 2 || !strings.HasPrefix(lines[0], "postgres://") {
t.Errorf("quiet should print only URLs, got:\n%s", out)
}
}

// TestSharedExposeCommandsRegistered guards that `shared expose` and
// `shared ports` are wired into the shared command tree.
func TestSharedExposeCommandsRegistered(t *testing.T) {
sh := newSharedCmd(&GlobalOpts{})
have := map[string]bool{}
for _, c := range sh.Commands() {
have[c.Name()] = true
}
for _, want := range []string{"expose", "ports", "status", "gc", "doctor"} {
if !have[want] {
t.Errorf("shared subcommand %q not registered", want)
}
// The top-level expose carries its --off flag.
c, _, _ := root.Find([]string{"expose"})
if c.Flags().Lookup("off") == nil {
t.Error("expose missing --off flag")
}
}
2 changes: 2 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ func NewRootCmd(opts Options) *cobra.Command {
newStatusCmd(g),
newUseCmd(g),
newContextCmd(g),
newExposeCmd(g),
newPortsCmd(g),
newShellInitCmd(g),
newLogsCmd(g),
newDashboardCmd(g),
Expand Down
6 changes: 4 additions & 2 deletions internal/cli/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ func newSharedCmd(g *GlobalOpts) *cobra.Command {
newSharedStatusCmd(g),
newSharedGcCmd(g),
newSharedDoctorCmd(g),
newSharedExposeCmd(g),
newSharedPortsCmd(g),
// `shared expose`/`shared ports` stay as aliases of the top-level `expose`/
// `ports` (fresh instances; same logic) for backward compatibility.
newExposeCmd(g),
newPortsCmd(g),
)
return cmd
}
Expand Down
Loading