Skip to content

Commit 50ba3ca

Browse files
looslabentranter
andauthored
databases: add get-ca to retrieve db certificate (#1580)
* databases: add get-ca to retrieve db certificate * return one certificate with -o json; encode certificate. * fix cert to make it work with -o text * add updated mocks * fix unit test: add get-ca to the list of db commands * update the description of the command and add tests * Update commands/databases.go Co-authored-by: Ben Tranter <[email protected]> --------- Co-authored-by: Ben Tranter <[email protected]>
1 parent 798111c commit 50ba3ca

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

commands/databases.go

+24
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ func Databases() *Command {
7070
- The date and time when the database cluster was created`+databaseListDetails, Writer, aliasOpt("g"), displayerType(&displayers.Databases{}))
7171
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`
7272

73+
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{}))
74+
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
75+
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` + "`"
76+
7377
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"
7478
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."
7579
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"
@@ -188,6 +192,21 @@ func RunDatabaseGet(c *CmdConfig) error {
188192
return displayDatabases(c, false, *db)
189193
}
190194

195+
// RunDatabaseGetCA returns a CA certificate for a database
196+
func RunDatabaseGetCA(c *CmdConfig) error {
197+
if len(c.Args) == 0 {
198+
return doctl.NewMissingArgsErr(c.NS)
199+
}
200+
201+
id := c.Args[0]
202+
dbCA, err := c.Databases().GetCA(id)
203+
if err != nil {
204+
return err
205+
}
206+
207+
return displayDatabaseCA(c, dbCA)
208+
}
209+
191210
// RunDatabaseCreate creates a database cluster
192211
func RunDatabaseCreate(c *CmdConfig) error {
193212
if len(c.Args) == 0 {
@@ -906,6 +925,11 @@ func displayDatabaseUsers(c *CmdConfig, users ...do.DatabaseUser) error {
906925
return c.Display(item)
907926
}
908927

928+
func displayDatabaseCA(c *CmdConfig, dbCA *do.DatabaseCA) error {
929+
item := &displayers.DatabaseCA{DatabaseCA: *dbCA}
930+
return c.Display(item)
931+
}
932+
909933
func databaseOptions() *Command {
910934
cmd := &Command{
911935
Command: &cobra.Command{

commands/databases_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ var (
6666
},
6767
}
6868

69+
testDBClusterCA = do.DatabaseCA{
70+
DatabaseCA: &godo.DatabaseCA{
71+
Certificate: []byte("QQDDC9jMGIwYzNiZS0wZjY4LTRkY4OCCAaIwDQYJKoZIhvcNAQ"),
72+
},
73+
}
74+
6975
testKafkaDBCluster = do.Database{
7076
Database: &godo.Database{
7177
ID: "ea93928g-8se0-929e-m1ns-029daj2k3j12",
@@ -244,6 +250,7 @@ func TestDatabasesCommand(t *testing.T) {
244250
assertCommandNames(t, cmd,
245251
"list",
246252
"get",
253+
"get-ca",
247254
"create",
248255
"delete",
249256
"connection",
@@ -378,6 +385,31 @@ func TestDatabasesGet(t *testing.T) {
378385
})
379386
}
380387

388+
func TestDatabasesGetCA(t *testing.T) {
389+
// Successful call
390+
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
391+
tm.databases.EXPECT().GetCA(testDBCluster.ID).Return(&testDBClusterCA, nil)
392+
config.Args = append(config.Args, testDBCluster.ID)
393+
err := RunDatabaseGetCA(config)
394+
assert.NoError(t, err)
395+
})
396+
397+
// Error
398+
notFound := "not-found"
399+
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
400+
tm.databases.EXPECT().GetCA(notFound).Return(nil, errTest)
401+
config.Args = append(config.Args, notFound)
402+
err := RunDatabaseGetCA(config)
403+
assert.Error(t, err)
404+
})
405+
406+
// ID not provided
407+
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
408+
err := RunDatabaseGetCA(config)
409+
assert.EqualError(t, doctl.NewMissingArgsErr(config.NS), err.Error())
410+
})
411+
}
412+
381413
func TestDatabasesList(t *testing.T) {
382414
// Successful call
383415
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {

commands/displayers/database.go

+30
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,36 @@ func (db *DatabaseBackups) KV() []map[string]any {
155155
return out
156156
}
157157

158+
type DatabaseCA struct {
159+
DatabaseCA do.DatabaseCA
160+
}
161+
162+
var _ Displayable = &DatabaseCA{}
163+
164+
func (dc *DatabaseCA) JSON(out io.Writer) error {
165+
return writeJSON(dc.DatabaseCA, out)
166+
}
167+
168+
func (dc *DatabaseCA) Cols() []string {
169+
return []string{
170+
"Certificate",
171+
}
172+
}
173+
174+
func (dc *DatabaseCA) ColMap() map[string]string {
175+
return map[string]string{
176+
"Certificate": "Certificate",
177+
}
178+
}
179+
180+
func (dc *DatabaseCA) KV() []map[string]any {
181+
return []map[string]any{
182+
{
183+
"Certificate": string(dc.DatabaseCA.Certificate),
184+
},
185+
}
186+
}
187+
158188
type DatabaseUsers struct {
159189
DatabaseUsers do.DatabaseUsers
160190
}

do/databases.go

+18
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ type Database struct {
2626
*godo.Database
2727
}
2828

29+
// DatabaseCA is a wrapper for godo.DatabaseCA
30+
type DatabaseCA struct {
31+
*godo.DatabaseCA
32+
}
33+
34+
// DatabaseCAs is a slice of DatabaseCA
35+
type DatabaseCAs []DatabaseCA
36+
2937
// Databases is a slice of Database
3038
type Databases []Database
3139

@@ -145,6 +153,7 @@ type DatabaseIndex struct {
145153
type DatabasesService interface {
146154
List() (Databases, error)
147155
Get(string) (*Database, error)
156+
GetCA(string) (*DatabaseCA, error)
148157
Create(*godo.DatabaseCreateRequest) (*Database, error)
149158
Delete(string) error
150159
GetConnection(string, bool) (*DatabaseConnection, error)
@@ -257,6 +266,15 @@ func (ds *databasesService) Get(databaseID string) (*Database, error) {
257266
return &Database{Database: db}, nil
258267
}
259268

269+
func (ds *databasesService) GetCA(databaseID string) (*DatabaseCA, error) {
270+
dbCA, _, err := ds.client.Databases.GetCA(context.TODO(), databaseID)
271+
if err != nil {
272+
return nil, err
273+
}
274+
275+
return &DatabaseCA{DatabaseCA: dbCA}, nil
276+
}
277+
260278
func (ds *databasesService) Create(req *godo.DatabaseCreateRequest) (*Database, error) {
261279
db, _, err := ds.client.Databases.Create(context.TODO(), req)
262280
if err != nil {

do/mocks/DatabasesService.go

+15
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)