Skip to content

Commit

Permalink
Enable config TiDB Cloud api host (#50)
Browse files Browse the repository at this point in the history
- enable config TiDB Cloud API URL. If not set, use `https://api.tidbcloud.com` as default.
- validate URL when set or using.
  • Loading branch information
xuanyu66 committed Dec 9, 2022
1 parent ed87f60 commit 6166a82
Show file tree
Hide file tree
Showing 18 changed files with 124 additions and 36 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ ticloud cluster create

Please check the CLI help for more information.

Documentation page is on the way.
### Set up TiDB Cloud API host

Usually you don't need to set up the TiDB Cloud API url, the default value is `https://api.tidbcloud.com`.

```shell
ticloud config set api-url https://api.tidbcloud.com
```

## Roadmap

Expand Down
9 changes: 6 additions & 3 deletions internal/cli/cluster/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ func CreateCmd(h *internal.Helper) *cobra.Command {
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
d := h.Client()
d, err := h.Client()
if err != nil {
return err
}

var clusterName string
var clusterType string
Expand Down Expand Up @@ -179,7 +182,7 @@ func CreateCmd(h *internal.Helper) *cobra.Command {
}
region = regionModel.(ui.SelectModel).Choices[regionModel.(ui.SelectModel).Selected].(string)

project, err := cloud.GetSelectedProject(h.QueryPageSize, h.Client())
project, err := cloud.GetSelectedProject(h.QueryPageSize, d)
if err != nil {
return err
}
Expand Down Expand Up @@ -241,7 +244,7 @@ func CreateCmd(h *internal.Helper) *cobra.Command {

clusterDefBody := &clusterApi.CreateClusterBody{}

err := clusterDefBody.UnmarshalBinary([]byte(fmt.Sprintf(`{
err = clusterDefBody.UnmarshalBinary([]byte(fmt.Sprintf(`{
"name": "%s",
"cluster_type": "%s",
"cloud_provider": "%s",
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/cluster/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ func (suite *CreateClusterSuite) SetupTest() {
var pageSize int64 = 10
suite.mockClient = new(mock.ApiClient)
suite.h = &internal.Helper{
Client: func() cloud.TiDBCloudClient {
return suite.mockClient
Client: func() (cloud.TiDBCloudClient, error) {
return suite.mockClient, nil
},
QueryPageSize: pageSize,
IOStreams: iostream.Test(),
Expand Down
11 changes: 7 additions & 4 deletions internal/cli/cluster/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ func DeleteCmd(h *internal.Helper) *cobra.Command {
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
d := h.Client()
d, err := h.Client()
if err != nil {
return err
}

var projectID string
var clusterID string
Expand All @@ -92,13 +95,13 @@ func DeleteCmd(h *internal.Helper) *cobra.Command {
}

// interactive mode
project, err := cloud.GetSelectedProject(h.QueryPageSize, h.Client())
project, err := cloud.GetSelectedProject(h.QueryPageSize, d)
if err != nil {
return err
}
projectID = project.ID

cluster, err := cloud.GetSelectedCluster(projectID, h.QueryPageSize, h.Client())
cluster, err := cloud.GetSelectedCluster(projectID, h.QueryPageSize, d)
if err != nil {
return err
}
Expand Down Expand Up @@ -147,7 +150,7 @@ func DeleteCmd(h *internal.Helper) *cobra.Command {
params := clusterApi.NewDeleteClusterParams().
WithProjectID(projectID).
WithClusterID(clusterID)
_, err := d.DeleteCluster(params)
_, err = d.DeleteCluster(params)
if err != nil {
return errors.Trace(err)
}
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/cluster/delete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func (suite *DeleteClusterSuite) SetupTest() {
var pageSize int64 = 10
suite.mockClient = new(mock.ApiClient)
suite.h = &internal.Helper{
Client: func() cloud.TiDBCloudClient {
return suite.mockClient
Client: func() (cloud.TiDBCloudClient, error) {
return suite.mockClient, nil
},
QueryPageSize: pageSize,
IOStreams: iostream.Test(),
Expand Down
9 changes: 6 additions & 3 deletions internal/cli/cluster/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ func DescribeCmd(h *internal.Helper) *cobra.Command {
return nil
},
RunE: func(cmd *cobra.Command, args []string) error {
d := h.Client()
d, err := h.Client()
if err != nil {
return err
}

var projectID string
var clusterID string
Expand All @@ -85,13 +88,13 @@ func DescribeCmd(h *internal.Helper) *cobra.Command {
}

// interactive mode
project, err := cloud.GetSelectedProject(h.QueryPageSize, h.Client())
project, err := cloud.GetSelectedProject(h.QueryPageSize, d)
if err != nil {
return err
}
projectID = project.ID

cluster, err := cloud.GetSelectedCluster(projectID, h.QueryPageSize, h.Client())
cluster, err := cloud.GetSelectedCluster(projectID, h.QueryPageSize, d)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/cluster/describe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ func (suite *DescribeClusterSuite) SetupTest() {
var pageSize int64 = 10
suite.mockClient = new(mock.ApiClient)
suite.h = &internal.Helper{
Client: func() cloud.TiDBCloudClient {
return suite.mockClient
Client: func() (cloud.TiDBCloudClient, error) {
return suite.mockClient, nil
},
QueryPageSize: pageSize,
IOStreams: iostream.Test(),
Expand Down
9 changes: 7 additions & 2 deletions internal/cli/cluster/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,19 @@ func ListCmd(h *internal.Helper) *cobra.Command {
}
},
RunE: func(cmd *cobra.Command, args []string) error {
d, err := h.Client()
if err != nil {
return err
}

var pID string
if opts.interactive {
if !h.IOStreams.CanPrompt {
return errors.New("The terminal doesn't support interactive mode, please use non-interactive mode")
}

// interactive mode
project, err := cloud.GetSelectedProject(h.QueryPageSize, h.Client())
project, err := cloud.GetSelectedProject(h.QueryPageSize, d)
if err != nil {
return err
}
Expand All @@ -71,7 +76,7 @@ func ListCmd(h *internal.Helper) *cobra.Command {
pID = args[0]
}

total, items, err := cloud.RetrieveClusters(pID, h.QueryPageSize, h.Client())
total, items, err := cloud.RetrieveClusters(pID, h.QueryPageSize, d)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/cluster/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ func (suite *ListClusterSuite) SetupTest() {
var pageSize int64 = 10
suite.mockClient = new(mock.ApiClient)
suite.h = &internal.Helper{
Client: func() cloud.TiDBCloudClient {
return suite.mockClient
Client: func() (cloud.TiDBCloudClient, error) {
return suite.mockClient, nil
},
QueryPageSize: pageSize,
IOStreams: iostream.Test(),
Expand Down
7 changes: 7 additions & 0 deletions internal/cli/config/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ If not, the config in the active profile will be set`, prop.ProfileProperties())
if curP == "" {
return fmt.Errorf("no profile is configured, please use `config create` to create a profile")
}

if propertyName == prop.ApiUrl {
_, err := prop.ValidateApiUrl(value)
if err != nil {
return err
}
}
viper.Set(fmt.Sprintf("%s.%s", curP, propertyName), value)
res = fmt.Sprintf("Set profile `%s` property `%s` to value `%s` successfully", curP, propertyName, value)
} else {
Expand Down
17 changes: 16 additions & 1 deletion internal/cli/config/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package config
import (
"bytes"
"fmt"
"net/url"
"os"
"testing"

Expand All @@ -25,6 +26,7 @@ import (
"tidbcloud-cli/internal/iostream"
"tidbcloud-cli/internal/util"

"github.com/juju/errors"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"
Expand Down Expand Up @@ -96,6 +98,15 @@ func (suite *SetConfigSuite) TestSetConfigArgs() {
args: []string{"unknown", "value"},
err: fmt.Errorf("unrecognized property `unknown`, use `config set --help` to find available properties"),
},
{
name: "set config with unknown property",
args: []string{"api-url", "baidu.com"},
err: errors.Annotate(&url.Error{
Op: "parse",
URL: "baidu.com",
Err: fmt.Errorf("invalid URI for request"),
}, "api url should format as <schema>://<host>"),
},
}

for _, tt := range tests {
Expand All @@ -108,7 +119,11 @@ func (suite *SetConfigSuite) TestSetConfigArgs() {
suite.h.IOStreams.Err.(*bytes.Buffer).Reset()
cmd.SetArgs(tt.args)
err = cmd.Execute()
assert.Equal(tt.err, err)
if err != nil {
assert.EqualError(tt.err, err.Error())
} else {
assert.Equal(tt.err, err)
}

assert.Equal(tt.stdoutString, suite.h.IOStreams.Out.(*bytes.Buffer).String())
assert.Equal(tt.stderrString, suite.h.IOStreams.Err.(*bytes.Buffer).String())
Expand Down
6 changes: 5 additions & 1 deletion internal/cli/project/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,11 @@ func ListCmd(h *internal.Helper) *cobra.Command {
List the projects with json format:
$ %[1]s project list -o json`, config.CliName),
RunE: func(cmd *cobra.Command, args []string) error {
total, items, err := cloud.RetrieveProjects(h.QueryPageSize, h.Client())
d, err := h.Client()
if err != nil {
return err
}
total, items, err := cloud.RetrieveProjects(h.QueryPageSize, d)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/project/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ func (suite *ListProjectSuite) SetupTest() {
var pageSize int64 = 10
suite.mockClient = new(mock.ApiClient)
suite.h = &internal.Helper{
Client: func() cloud.TiDBCloudClient {
return suite.mockClient
Client: func() (cloud.TiDBCloudClient, error) {
return suite.mockClient, nil
},
QueryPageSize: pageSize,
IOStreams: iostream.Test(),
Expand Down
13 changes: 11 additions & 2 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,18 @@ func Execute(ctx context.Context, ver, commit, buildDate string) {
}

h := &internal.Helper{
Client: func() cloud.TiDBCloudClient {
Client: func() (cloud.TiDBCloudClient, error) {
publicKey, privateKey := util.GetAccessKeys(c.ActiveProfile)
return cloud.NewClientDelegate(publicKey, privateKey)
apiUrl := util.GetApiUrl(c.ActiveProfile)
// If the user has not set the api url, use the default one.
if apiUrl == "" {
apiUrl = cloud.DefaultApiUrl
}
delegate, err := cloud.NewClientDelegate(publicKey, privateKey, apiUrl)
if err != nil {
return nil, err
}
return delegate, nil
},
QueryPageSize: internal.DefaultPageSize,
IOStreams: iostream.System(),
Expand Down
2 changes: 1 addition & 1 deletion internal/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const (
)

type Helper struct {
Client func() cloud.TiDBCloudClient
Client func() (cloud.TiDBCloudClient, error)
QueryPageSize int64
IOStreams *iostream.IOStreams
Config *config.Config
Expand Down
19 changes: 17 additions & 2 deletions internal/prop/property.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,35 @@

package prop

import (
"net/url"

"github.com/juju/errors"
)

const (
PublicKey string = "public-key"
PrivateKey string = "private-key"
CurProfile string = "current-profile"
ApiUrl string = "api-url"
)

func GlobalProperties() []string {
return []string{CurProfile}
}

func ProfileProperties() []string {
return []string{PublicKey, PrivateKey}
return []string{PublicKey, PrivateKey, ApiUrl}
}

func Properties() []string {
return []string{PublicKey, PrivateKey, CurProfile}
return []string{PublicKey, PrivateKey, CurProfile, ApiUrl}
}

func ValidateApiUrl(value string) (*url.URL, error) {
u, err := url.ParseRequestURI(value)
if err != nil {
return nil, errors.Annotate(err, "api url should format as <schema>://<host>")
}
return u, nil
}
25 changes: 19 additions & 6 deletions internal/service/cloud/api_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package cloud
import (
"net/http"

"tidbcloud-cli/internal/prop"

apiClient "github.com/c4pt0r/go-tidbcloud-sdk-v1/client"
"github.com/c4pt0r/go-tidbcloud-sdk-v1/client/cluster"
"github.com/c4pt0r/go-tidbcloud-sdk-v1/client/project"
Expand All @@ -26,7 +28,7 @@ import (
)

const (
apiBaseUrl = "api.tidbcloud.com"
DefaultApiUrl = "https://api.tidbcloud.com"
)

type TiDBCloudClient interface {
Expand All @@ -47,10 +49,14 @@ type ClientDelegate struct {
c *apiClient.GoTidbcloud
}

func NewClientDelegate(publicKey string, privateKey string) *ClientDelegate {
return &ClientDelegate{
c: NewApiClient(publicKey, privateKey),
func NewClientDelegate(publicKey string, privateKey string, apiUrl string) (*ClientDelegate, error) {
client, err := NewApiClient(publicKey, privateKey, apiUrl)
if err != nil {
return nil, err
}
return &ClientDelegate{
c: client,
}, nil
}

func (d *ClientDelegate) CreateCluster(params *cluster.CreateClusterParams, opts ...cluster.ClientOption) (*cluster.CreateClusterOK, error) {
Expand All @@ -77,12 +83,19 @@ func (d *ClientDelegate) ListProjects(params *project.ListProjectsParams, opts .
return d.c.Project.ListProjects(params, opts...)
}

func NewApiClient(publicKey string, privateKey string) *apiClient.GoTidbcloud {
func NewApiClient(publicKey string, privateKey string, apiUrl string) (*apiClient.GoTidbcloud, error) {
httpclient := &http.Client{
Transport: &digest.Transport{
Username: publicKey,
Password: privateKey,
},
}
return apiClient.New(httpTransport.NewWithClient(apiBaseUrl, "/", []string{"https"}, httpclient), strfmt.Default)

// Parse the URL
u, err := prop.ValidateApiUrl(apiUrl)
if err != nil {
return nil, err
}

return apiClient.New(httpTransport.NewWithClient(u.Host, u.Path, []string{u.Scheme}, httpclient), strfmt.Default), nil
}
Loading

0 comments on commit 6166a82

Please sign in to comment.