Skip to content

Commit

Permalink
Merge branch 'main' into alushnikova/update-contributing
Browse files Browse the repository at this point in the history
  • Loading branch information
loosla authored Sep 13, 2024
2 parents d1def6c + 50ba3ca commit 4766510
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 0 deletions.
24 changes: 24 additions & 0 deletions commands/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ func Databases() *Command {
- The date and time when the database cluster was created`+databaseListDetails, Writer, aliasOpt("g"), displayerType(&displayers.Databases{}))
cmdDatabaseGet.Example = `The following example retrieves the details for a database cluster with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + ` and uses the ` + "`" + `--format` + "`" + ` flag to return only the database's ID, engine, and engine version: doctl databases get f81d4fae-7dec-11d0-a765-00a0c91e6bf6`

cmdDatabaseGetCA := CmdBuilder(cmd, RunDatabaseGetCA, "get-ca <database-cluster-id>", "Provides the CA certificate for a DigitalOcean database", `Retrieves a database certificate`, Writer, aliasOpt("gc"), displayerType(&displayers.DatabaseCA{}))
cmdDatabaseGetCA.Example = `Retrieves the database certificate for the cluster with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + `: doctl databases get-ca f81d4fae-7dec-11d0-a765-00a0c91e6bf6
With the ` + "`" + `-o json flag` + "`" + `, the certificate to connect to the database is base64 encoded. To decode it: ` + "`" + `doctl databases get-ca <database-cluster-id> -o json | jq -r .certificate | base64 --decode` + "`"

nodeSizeDetails := "The size of the nodes in the database cluster, for example `db-s-1vcpu-1gb` indicates a 1 CPU, 1GB node. For a list of available size slugs, visit: https://docs.digitalocean.com/reference/api/api-reference/#tag/Databases"
nodeNumberDetails := "The number of nodes in the database cluster. Valid values are 1-3. In addition to the primary node, up to two standby nodes may be added for high availability."
storageSizeMiBDetails := "The amount of disk space allocated to the cluster. Applicable for PostgreSQL and MySQL clusters. Each plan size has a default value but can be increased in increments up to a maximum amount. For ranges, visit: https://www.digitalocean.com/pricing/managed-databases"
Expand Down Expand Up @@ -188,6 +192,21 @@ func RunDatabaseGet(c *CmdConfig) error {
return displayDatabases(c, false, *db)
}

// RunDatabaseGetCA returns a CA certificate for a database
func RunDatabaseGetCA(c *CmdConfig) error {
if len(c.Args) == 0 {
return doctl.NewMissingArgsErr(c.NS)
}

id := c.Args[0]
dbCA, err := c.Databases().GetCA(id)
if err != nil {
return err
}

return displayDatabaseCA(c, dbCA)
}

// RunDatabaseCreate creates a database cluster
func RunDatabaseCreate(c *CmdConfig) error {
if len(c.Args) == 0 {
Expand Down Expand Up @@ -906,6 +925,11 @@ func displayDatabaseUsers(c *CmdConfig, users ...do.DatabaseUser) error {
return c.Display(item)
}

func displayDatabaseCA(c *CmdConfig, dbCA *do.DatabaseCA) error {
item := &displayers.DatabaseCA{DatabaseCA: *dbCA}
return c.Display(item)
}

func databaseOptions() *Command {
cmd := &Command{
Command: &cobra.Command{
Expand Down
32 changes: 32 additions & 0 deletions commands/databases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ var (
},
}

testDBClusterCA = do.DatabaseCA{
DatabaseCA: &godo.DatabaseCA{
Certificate: []byte("QQDDC9jMGIwYzNiZS0wZjY4LTRkY4OCCAaIwDQYJKoZIhvcNAQ"),
},
}

testKafkaDBCluster = do.Database{
Database: &godo.Database{
ID: "ea93928g-8se0-929e-m1ns-029daj2k3j12",
Expand Down Expand Up @@ -244,6 +250,7 @@ func TestDatabasesCommand(t *testing.T) {
assertCommandNames(t, cmd,
"list",
"get",
"get-ca",
"create",
"delete",
"connection",
Expand Down Expand Up @@ -378,6 +385,31 @@ func TestDatabasesGet(t *testing.T) {
})
}

func TestDatabasesGetCA(t *testing.T) {
// Successful call
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.databases.EXPECT().GetCA(testDBCluster.ID).Return(&testDBClusterCA, nil)
config.Args = append(config.Args, testDBCluster.ID)
err := RunDatabaseGetCA(config)
assert.NoError(t, err)
})

// Error
notFound := "not-found"
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
tm.databases.EXPECT().GetCA(notFound).Return(nil, errTest)
config.Args = append(config.Args, notFound)
err := RunDatabaseGetCA(config)
assert.Error(t, err)
})

// ID not provided
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
err := RunDatabaseGetCA(config)
assert.EqualError(t, doctl.NewMissingArgsErr(config.NS), err.Error())
})
}

func TestDatabasesList(t *testing.T) {
// Successful call
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
Expand Down
30 changes: 30 additions & 0 deletions commands/displayers/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,36 @@ func (db *DatabaseBackups) KV() []map[string]any {
return out
}

type DatabaseCA struct {
DatabaseCA do.DatabaseCA
}

var _ Displayable = &DatabaseCA{}

func (dc *DatabaseCA) JSON(out io.Writer) error {
return writeJSON(dc.DatabaseCA, out)
}

func (dc *DatabaseCA) Cols() []string {
return []string{
"Certificate",
}
}

func (dc *DatabaseCA) ColMap() map[string]string {
return map[string]string{
"Certificate": "Certificate",
}
}

func (dc *DatabaseCA) KV() []map[string]any {
return []map[string]any{
{
"Certificate": string(dc.DatabaseCA.Certificate),
},
}
}

type DatabaseUsers struct {
DatabaseUsers do.DatabaseUsers
}
Expand Down
18 changes: 18 additions & 0 deletions do/databases.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ type Database struct {
*godo.Database
}

// DatabaseCA is a wrapper for godo.DatabaseCA
type DatabaseCA struct {
*godo.DatabaseCA
}

// DatabaseCAs is a slice of DatabaseCA
type DatabaseCAs []DatabaseCA

// Databases is a slice of Database
type Databases []Database

Expand Down Expand Up @@ -145,6 +153,7 @@ type DatabaseIndex struct {
type DatabasesService interface {
List() (Databases, error)
Get(string) (*Database, error)
GetCA(string) (*DatabaseCA, error)
Create(*godo.DatabaseCreateRequest) (*Database, error)
Delete(string) error
GetConnection(string, bool) (*DatabaseConnection, error)
Expand Down Expand Up @@ -257,6 +266,15 @@ func (ds *databasesService) Get(databaseID string) (*Database, error) {
return &Database{Database: db}, nil
}

func (ds *databasesService) GetCA(databaseID string) (*DatabaseCA, error) {
dbCA, _, err := ds.client.Databases.GetCA(context.TODO(), databaseID)
if err != nil {
return nil, err
}

return &DatabaseCA{DatabaseCA: dbCA}, nil
}

func (ds *databasesService) Create(req *godo.DatabaseCreateRequest) (*Database, error) {
db, _, err := ds.client.Databases.Create(context.TODO(), req)
if err != nil {
Expand Down
15 changes: 15 additions & 0 deletions do/mocks/DatabasesService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4766510

Please sign in to comment.