Skip to content

Commit 9f55bc3

Browse files
authored
Merge a0fda70 into 641eb1c
2 parents 641eb1c + a0fda70 commit 9f55bc3

File tree

10 files changed

+896
-1
lines changed

10 files changed

+896
-1
lines changed

internal/command/launch/plan/postgres_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,14 @@ func (m *mockUIEXClient) RestoreManagedClusterBackup(ctx context.Context, cluste
111111
return uiex.RestoreManagedClusterBackupResponse{}, nil
112112
}
113113

114+
func (m *mockUIEXClient) CreateAttachment(ctx context.Context, clusterId string, input uiex.CreateAttachmentInput) (uiex.CreateAttachmentResponse, error) {
115+
return uiex.CreateAttachmentResponse{}, nil
116+
}
117+
118+
func (m *mockUIEXClient) DeleteAttachment(ctx context.Context, clusterId string, appName string) (uiex.DeleteAttachmentResponse, error) {
119+
return uiex.DeleteAttachmentResponse{}, nil
120+
}
121+
114122
func (m *mockUIEXClient) CreateBuild(ctx context.Context, in uiex.CreateBuildRequest) (*uiex.BuildResponse, error) {
115123
return &uiex.BuildResponse{}, nil
116124
}

internal/command/mpg/attach.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,15 @@ func runAttach(ctx context.Context) error {
281281
return err
282282
}
283283

284+
// Create attachment record to track the cluster-app relationship
285+
attachInput := uiex.CreateAttachmentInput{
286+
AppName: appName,
287+
}
288+
if _, err := uiexClient.CreateAttachment(ctx, cluster.Id, attachInput); err != nil {
289+
// Log warning but don't fail - the secret was set successfully
290+
fmt.Fprintf(io.ErrOut, "Warning: failed to create attachment record: %v\n", err)
291+
}
292+
284293
fmt.Fprintf(io.Out, "\nPostgres cluster %s is being attached to %s\n", cluster.Id, appName)
285294
fmt.Fprintf(io.Out, "The following secret was added to %s:\n %s=%s\n", appName, variableName, connectionUri)
286295

internal/command/mpg/detach.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package mpg
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/spf13/cobra"
8+
"github.com/superfly/flyctl/internal/appconfig"
9+
"github.com/superfly/flyctl/internal/command"
10+
"github.com/superfly/flyctl/internal/flag"
11+
"github.com/superfly/flyctl/internal/flyutil"
12+
"github.com/superfly/flyctl/internal/uiexutil"
13+
"github.com/superfly/flyctl/iostreams"
14+
)
15+
16+
func newDetach() *cobra.Command {
17+
const (
18+
short = "Detach a managed Postgres cluster from an app"
19+
long = short + ". " +
20+
`This command will remove the attachment record linking the app to the cluster.
21+
Note: This does NOT remove any secrets from the app. Use 'fly secrets unset' to remove secrets.`
22+
usage = "detach <CLUSTER ID>"
23+
)
24+
25+
cmd := command.New(usage, short, long, runDetach,
26+
command.RequireSession,
27+
command.RequireAppName,
28+
)
29+
cmd.Args = cobra.MaximumNArgs(1)
30+
31+
flag.Add(cmd,
32+
flag.App(),
33+
flag.AppConfig(),
34+
flag.Yes(),
35+
)
36+
37+
return cmd
38+
}
39+
40+
func runDetach(ctx context.Context) error {
41+
// Check token compatibility early
42+
if err := validateMPGTokenCompatibility(ctx); err != nil {
43+
return err
44+
}
45+
46+
var (
47+
clusterId = flag.FirstArg(ctx)
48+
appName = appconfig.NameFromContext(ctx)
49+
client = flyutil.ClientFromContext(ctx)
50+
io = iostreams.FromContext(ctx)
51+
)
52+
53+
// Get app details to determine which org it belongs to
54+
app, err := client.GetAppBasic(ctx, appName)
55+
if err != nil {
56+
return fmt.Errorf("failed retrieving app %s: %w", appName, err)
57+
}
58+
59+
appOrgSlug := app.Organization.RawSlug
60+
if appOrgSlug != "" && clusterId == "" {
61+
fmt.Fprintf(io.Out, "Listing clusters in organization %s\n", appOrgSlug)
62+
}
63+
64+
// Get cluster details
65+
cluster, _, err := ClusterFromArgOrSelect(ctx, clusterId, appOrgSlug)
66+
if err != nil {
67+
return fmt.Errorf("failed retrieving cluster %s: %w", clusterId, err)
68+
}
69+
70+
clusterOrgSlug := cluster.Organization.Slug
71+
72+
// Verify that the app and cluster are in the same organization
73+
if appOrgSlug != clusterOrgSlug {
74+
return fmt.Errorf("app %s is in organization %s, but cluster %s is in organization %s. They must be in the same organization",
75+
appName, appOrgSlug, cluster.Id, clusterOrgSlug)
76+
}
77+
78+
uiexClient := uiexutil.ClientFromContext(ctx)
79+
80+
// Delete the attachment record
81+
_, err = uiexClient.DeleteAttachment(ctx, cluster.Id, appName)
82+
if err != nil {
83+
return fmt.Errorf("failed to detach: %w", err)
84+
}
85+
86+
fmt.Fprintf(io.Out, "\nPostgres cluster %s has been detached from %s\n", cluster.Id, appName)
87+
fmt.Fprintf(io.Out, "Note: This only removes the attachment record. Any secrets (like DATABASE_URL) are still set on the app.\n")
88+
fmt.Fprintf(io.Out, "Use 'fly secrets unset DATABASE_URL -a %s' to remove the connection string.\n", appName)
89+
90+
return nil
91+
}

internal/command/mpg/list.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ package mpg
33
import (
44
"context"
55
"fmt"
6+
"strings"
67

78
"github.com/spf13/cobra"
89

910
"github.com/superfly/flyctl/gql"
11+
"github.com/superfly/flyctl/internal/uiex"
1012
"github.com/superfly/flyctl/iostreams"
1113

1214
"github.com/superfly/flyctl/internal/command"
@@ -95,8 +97,22 @@ func runList(ctx context.Context) error {
9597
cluster.Region,
9698
cluster.Status,
9799
cluster.Plan,
100+
formatAttachedApps(cluster.AttachedApps),
98101
})
99102
}
100103

101-
return render.Table(out, "", rows, "ID", "Name", "Org", "Region", "Status", "Plan")
104+
return render.Table(out, "", rows, "ID", "Name", "Org", "Region", "Status", "Plan", "Attached Apps")
105+
}
106+
107+
// formatAttachedApps formats the list of attached apps for display
108+
func formatAttachedApps(apps []uiex.AttachedApp) string {
109+
if len(apps) == 0 {
110+
return "<no attached apps>"
111+
}
112+
113+
names := make([]string, len(apps))
114+
for i, app := range apps {
115+
names[i] = app.Name
116+
}
117+
return strings.Join(names, ", ")
102118
}

internal/command/mpg/mpg.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ func New() *cobra.Command {
7676
newProxy(),
7777
newConnect(),
7878
newAttach(),
79+
newDetach(),
7980
newStatus(),
8081
newList(),
8182
newCreate(),

0 commit comments

Comments
 (0)