Skip to content

Commit abda034

Browse files
committed
implemnet
1 parent 13771fa commit abda034

24 files changed

+2688
-22
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package privatelink
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/AlecAivazis/survey/v2"
8+
"github.com/fatih/color"
9+
"github.com/juju/errors"
10+
"github.com/spf13/cobra"
11+
12+
"github.com/tidbcloud/tidbcloud-cli/internal"
13+
"github.com/tidbcloud/tidbcloud-cli/internal/config"
14+
"github.com/tidbcloud/tidbcloud-cli/internal/flag"
15+
"github.com/tidbcloud/tidbcloud-cli/internal/output"
16+
"github.com/tidbcloud/tidbcloud-cli/internal/service/cloud"
17+
plapi "github.com/tidbcloud/tidbcloud-cli/pkg/tidbcloud/v1beta1/serverless/privatelink"
18+
)
19+
20+
type AttachDomainOpts struct {
21+
interactive bool
22+
}
23+
24+
func (o AttachDomainOpts) NonInteractiveFlags() []string {
25+
return []string{
26+
flag.ClusterID,
27+
flag.PrivateLinkConnectionID,
28+
flag.PLCAttachDomainType,
29+
flag.PLCAttachDomainUniqueName,
30+
}
31+
}
32+
33+
func (o AttachDomainOpts) RequiredFlags() []string {
34+
return []string{
35+
flag.ClusterID,
36+
flag.PrivateLinkConnectionID,
37+
flag.PLCAttachDomainType,
38+
}
39+
}
40+
41+
func (o *AttachDomainOpts) MarkInteractive(cmd *cobra.Command) error {
42+
o.interactive = true
43+
for _, fn := range o.NonInteractiveFlags() {
44+
if f := cmd.Flags().Lookup(fn); f != nil && f.Changed {
45+
o.interactive = false
46+
}
47+
}
48+
if !o.interactive {
49+
for _, fn := range o.RequiredFlags() {
50+
if err := cmd.MarkFlagRequired(fn); err != nil {
51+
return err
52+
}
53+
}
54+
}
55+
return nil
56+
}
57+
58+
func AttachDomainCmd(h *internal.Helper) *cobra.Command {
59+
opts := &AttachDomainOpts{interactive: true}
60+
cmd := &cobra.Command{
61+
Use: "attach",
62+
Short: "Attach domains to a private link connection",
63+
Args: cobra.NoArgs,
64+
Example: fmt.Sprintf(` Attach domain (interactive):
65+
$ %[1]s serverless private-link-connection attach
66+
67+
Attach domain (non-interactive):
68+
$ %[1]s serverless private-link-connection attach -c <cluster-id> --private-link-connection-id <plc-id> --type <type> --unique-name <unique-name>`, config.CliName),
69+
PreRunE: func(cmd *cobra.Command, args []string) error {
70+
return opts.MarkInteractive(cmd)
71+
},
72+
RunE: func(cmd *cobra.Command, args []string) error {
73+
d, err := h.Client()
74+
if err != nil {
75+
return err
76+
}
77+
ctx := cmd.Context()
78+
79+
var clusterID, plcID string
80+
var domainType plapi.PrivateLinkConnectionDomainTypeEnum
81+
var uniqueName string
82+
var dryRun bool
83+
dryRun, err = cmd.Flags().GetBool(flag.PLCAttachDomainDryRun)
84+
if err != nil {
85+
return errors.Trace(err)
86+
}
87+
if opts.interactive {
88+
if !h.IOStreams.CanPrompt {
89+
return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode")
90+
}
91+
92+
project, err := cloud.GetSelectedProject(ctx, h.QueryPageSize, d)
93+
if err != nil {
94+
return err
95+
}
96+
cluster, err := cloud.GetSelectedCluster(ctx, project.ID, h.QueryPageSize, d)
97+
if err != nil {
98+
return err
99+
}
100+
clusterID = cluster.ID
101+
102+
privatelink, err := cloud.GetSelectedPrivateLinkConnection(ctx, cluster.ID, int32(h.QueryPageSize), d)
103+
if err != nil {
104+
return err
105+
}
106+
plcID = privatelink.ID
107+
108+
domainType, err = GetSelectedPLCAttachDomainType()
109+
if err != nil {
110+
return err
111+
}
112+
113+
switch domainType {
114+
case plapi.PRIVATELINKCONNECTIONDOMAINTYPEENUM_CONFLUENT:
115+
if err := survey.AskOne(&survey.Input{Message: "Domain unique name:"}, &uniqueName); err != nil {
116+
return err
117+
}
118+
case plapi.PRIVATELINKCONNECTIONDOMAINTYPEENUM_TIDBCLOUD_MANAGED:
119+
if !dryRun {
120+
if err := survey.AskOne(&survey.Input{Message: "Domain unique name:"}, &uniqueName); err != nil {
121+
return err
122+
}
123+
}
124+
default:
125+
return errors.New("invalid domain type")
126+
}
127+
} else {
128+
var err error
129+
clusterID, err = cmd.Flags().GetString(flag.ClusterID)
130+
if err != nil {
131+
return errors.Trace(err)
132+
}
133+
plcID, err = cmd.Flags().GetString(flag.PrivateLinkConnectionID)
134+
if err != nil {
135+
return errors.Trace(err)
136+
}
137+
domainTypeStr, err := cmd.Flags().GetString(flag.PLCAttachDomainType)
138+
if err != nil {
139+
return errors.Trace(err)
140+
}
141+
domainType = plapi.PrivateLinkConnectionDomainTypeEnum(domainTypeStr)
142+
uniqueName, err = cmd.Flags().GetString(flag.PLCAttachDomainUniqueName)
143+
if err != nil {
144+
return errors.Trace(err)
145+
}
146+
}
147+
148+
if domainType == "" {
149+
return errors.New("domain type is required")
150+
}
151+
152+
body := &plapi.PrivateLinkConnectionServiceAttachDomainsBody{
153+
AttachDomain: plapi.AttachDomain{
154+
Type: domainType,
155+
UniqueName: &uniqueName,
156+
},
157+
DryRun: &dryRun,
158+
}
159+
160+
resp, err := d.AttachPrivateLinkDomains(ctx, clusterID, plcID, body)
161+
if err != nil {
162+
return errors.Trace(err)
163+
}
164+
if !dryRun {
165+
return output.PrintJson(h.IOStreams.Out, resp)
166+
}
167+
domains := make([]string, 0, len(resp.Domains))
168+
for _, domain := range resp.Domains {
169+
domains = append(domains, *domain.Name)
170+
}
171+
fmt.Fprintf(h.IOStreams.Out, "unique name %s:\n%s\n",
172+
color.BlueString("%v", *resp.UniqueName),
173+
color.GreenString("%v", strings.Join(domains, "\n")))
174+
return nil
175+
},
176+
}
177+
178+
cmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "The cluster ID.")
179+
cmd.Flags().String(flag.PrivateLinkConnectionID, "", "The private link connection ID.")
180+
cmd.Flags().String(flag.PLCAttachDomainType, "", fmt.Sprintf("The type of domain to attach, one of: %v", plapi.AllowedPrivateLinkConnectionDomainTypeEnumEnumValues))
181+
cmd.Flags().String(flag.PLCAttachDomainUniqueName, "", "The unique name of the domain to attach, you can use --dry-run to generate the unique name when attaching a TiDB Cloud managed domain.")
182+
cmd.Flags().Bool(flag.PLCAttachDomainDryRun, false, "set dry run mode to only show generated domains without attaching them.")
183+
184+
return cmd
185+
}

internal/cli/serverless/privatelink/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func DeleteCmd(h *internal.Helper) *cobra.Command {
7171
$ %[1]s serverless private-link-connection delete
7272
7373
Delete a private link connection (non-interactive):
74-
$ %[1]s serverless private-link-connection delete -c <cluster-id> -p <private-link-connection-id>`, config.CliName),
74+
$ %[1]s serverless private-link-connection delete -c <cluster-id> --private-link-connection-id <private-link-connection-id>`, config.CliName),
7575
PreRunE: func(cmd *cobra.Command, args []string) error {
7676
return opts.MarkInteractive(cmd)
7777
},

internal/cli/serverless/privatelink/describe.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func DescribeCmd(h *internal.Helper) *cobra.Command {
6868
$ %[1]s serverless private-link-connection describe
6969
7070
Describe a private link connection (non-interactive):
71-
$ %[1]s serverless private-link-connection describe -c <cluster-id> -p <private-link-connection-id>`, config.CliName),
71+
$ %[1]s serverless private-link-connection describe -c <cluster-id> --private-link-connection-id <private-link-connection-id>`, config.CliName),
7272
PreRunE: func(cmd *cobra.Command, args []string) error {
7373
return opts.MarkInteractive(cmd)
7474
},
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package privatelink
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/fatih/color"
8+
"github.com/juju/errors"
9+
"github.com/spf13/cobra"
10+
11+
"github.com/tidbcloud/tidbcloud-cli/internal"
12+
"github.com/tidbcloud/tidbcloud-cli/internal/config"
13+
"github.com/tidbcloud/tidbcloud-cli/internal/flag"
14+
"github.com/tidbcloud/tidbcloud-cli/internal/service/cloud"
15+
plapi "github.com/tidbcloud/tidbcloud-cli/pkg/tidbcloud/v1beta1/serverless/privatelink"
16+
)
17+
18+
type DetachDomainOpts struct {
19+
interactive bool
20+
}
21+
22+
func (o DetachDomainOpts) NonInteractiveFlags() []string {
23+
return []string{
24+
flag.ClusterID,
25+
flag.PrivateLinkConnectionID,
26+
flag.PLCAttachDomainID,
27+
}
28+
}
29+
30+
func (o *DetachDomainOpts) MarkInteractive(cmd *cobra.Command) error {
31+
o.interactive = true
32+
for _, fn := range o.NonInteractiveFlags() {
33+
if f := cmd.Flags().Lookup(fn); f != nil && f.Changed {
34+
o.interactive = false
35+
}
36+
}
37+
if !o.interactive {
38+
for _, fn := range o.NonInteractiveFlags() {
39+
if err := cmd.MarkFlagRequired(fn); err != nil {
40+
return err
41+
}
42+
}
43+
}
44+
return nil
45+
}
46+
47+
func DetachDomainCmd(h *internal.Helper) *cobra.Command {
48+
opts := &DetachDomainOpts{interactive: true}
49+
50+
cmd := &cobra.Command{
51+
Use: "detach",
52+
Short: "Detach domains from a private link connection",
53+
Args: cobra.NoArgs,
54+
Example: fmt.Sprintf(` Detach domains (interactive):
55+
$ %[1]s serverless private-link-connection detach
56+
57+
Detach domains (non-interactive):
58+
$ %[1]s serverless private-link-connection detach -c <cluster-id> --private-link-connection-id <plc-id> --plc-attach-domain-id <attach-id>`, config.CliName),
59+
PreRunE: func(cmd *cobra.Command, args []string) error {
60+
return opts.MarkInteractive(cmd)
61+
},
62+
RunE: func(cmd *cobra.Command, args []string) error {
63+
d, err := h.Client()
64+
if err != nil {
65+
return err
66+
}
67+
ctx := cmd.Context()
68+
69+
var clusterID, plcID, attachID string
70+
if opts.interactive {
71+
if !h.IOStreams.CanPrompt {
72+
return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode")
73+
}
74+
project, err := cloud.GetSelectedProject(ctx, h.QueryPageSize, d)
75+
if err != nil {
76+
return err
77+
}
78+
cluster, err := cloud.GetSelectedCluster(ctx, project.ID, h.QueryPageSize, d)
79+
if err != nil {
80+
return err
81+
}
82+
clusterID = cluster.ID
83+
privatelink, err := cloud.GetSelectedPrivateLinkConnection(ctx, cluster.ID, int32(h.QueryPageSize), d)
84+
if err != nil {
85+
return err
86+
}
87+
plcID = privatelink.ID
88+
attach, err := cloud.GetSelectedAttachDomain(ctx, cluster.ID, privatelink.ID, d)
89+
if err != nil {
90+
return err
91+
}
92+
attachID = attach.ID
93+
} else {
94+
var err error
95+
clusterID, err = cmd.Flags().GetString(flag.ClusterID)
96+
if err != nil {
97+
return errors.Trace(err)
98+
}
99+
plcID, err = cmd.Flags().GetString(flag.PrivateLinkConnectionID)
100+
if err != nil {
101+
return errors.Trace(err)
102+
}
103+
attachID, err = cmd.Flags().GetString(flag.PLCAttachDomainID)
104+
if err != nil {
105+
return errors.Trace(err)
106+
}
107+
}
108+
109+
body := &plapi.PrivateLinkConnectionServiceDetachDomainsBody{
110+
AttachDomainId: attachID,
111+
}
112+
resp, err := d.DetachPrivateLinkDomains(ctx, clusterID, plcID, body)
113+
if err != nil {
114+
return errors.Trace(err)
115+
}
116+
domains := make([]string, 0, len(resp.Domains))
117+
for _, domain := range resp.Domains {
118+
domains = append(domains, *domain.Name)
119+
}
120+
fmt.Fprintf(h.IOStreams.Out, "Successfully detached domains:\n%s\n", color.GreenString("%v", strings.Join(domains, "\n")))
121+
return nil
122+
},
123+
}
124+
125+
cmd.Flags().StringP(flag.ClusterID, flag.ClusterIDShort, "", "The cluster ID.")
126+
cmd.Flags().String(flag.PrivateLinkConnectionID, "", "The private link connection ID.")
127+
cmd.Flags().String(flag.PLCAttachDomainID, "", "The private link connection attach domain ID.")
128+
return cmd
129+
}

internal/cli/serverless/privatelink/privatelink.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ func PrivateLinkConnectionCmd(h *internal.Helper) *cobra.Command {
3333
DeleteCmd(h),
3434
ListCmd(h),
3535
GetZonesCmd(h),
36+
AttachDomainCmd(h),
37+
DetachDomainCmd(h),
3638
)
3739

3840
return cmd

internal/cli/serverless/privatelink/ui.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,28 @@ func GetSelectedPrivateLinkConnectionType() (privatelink.PrivateLinkConnectionTy
5454
}
5555
return resp.(privatelink.PrivateLinkConnectionTypeEnum), nil
5656
}
57+
58+
func GetSelectedPLCAttachDomainType() (privatelink.PrivateLinkConnectionDomainTypeEnum, error) {
59+
types := make([]interface{}, 0, len(privatelink.AllowedPrivateLinkConnectionDomainTypeEnumEnumValues))
60+
for _, v := range privatelink.AllowedPrivateLinkConnectionDomainTypeEnumEnumValues {
61+
types = append(types, v)
62+
}
63+
selectModel, err := ui.InitialSelectModel(types, "Choose the private link connection domain type:")
64+
if err != nil {
65+
return "", errors.Trace(err)
66+
}
67+
p := tea.NewProgram(selectModel)
68+
model, err := p.Run()
69+
if err != nil {
70+
return "", errors.Trace(err)
71+
}
72+
if m, _ := model.(ui.SelectModel); m.Interrupted {
73+
return "", util.InterruptError
74+
}
75+
76+
resp := model.(ui.SelectModel).GetSelectedItem()
77+
if resp == nil {
78+
return "", errors.New("no private link connection domain type selected")
79+
}
80+
return resp.(privatelink.PrivateLinkConnectionDomainTypeEnum), nil
81+
}

internal/flag/flag.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ const (
124124
AWSEndpointServiceName string = "aws.endpoint-service-name"
125125
AWSEndpointServiceRegion string = "aws.endpoint-service-region"
126126
AlicloudEndpointServiceName string = "alicloud.endpoint-service-name"
127+
PLCAttachDomainType string = "type"
128+
PLCAttachDomainUniqueName string = "unique-name"
129+
PLCAttachDomainID string = "plc-attach-domain-id"
130+
PLCAttachDomainDryRun string = "dry-run"
127131
)
128132

129133
const OutputHelp = "Output format, one of [\"human\" \"json\"]. For the complete result, please use json format."

0 commit comments

Comments
 (0)