diff --git a/Dockerfile b/Dockerfile index 255b642d8..5c862a241 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Step 1: Build -FROM golang:1.21-alpine AS build +FROM golang:1.22-alpine AS build ARG GOARCH=amd64 ENV OUT_D /out diff --git a/commands/databases.go b/commands/databases.go index 5eba81dcf..18ca9a586 100644 --- a/commands/databases.go +++ b/commands/databases.go @@ -156,6 +156,7 @@ For PostgreSQL and MySQL clusters, you can also provide a disk size in MiB to sc cmd.AddCommand(databaseConfiguration()) cmd.AddCommand(databaseTopic()) cmd.AddCommand(databaseEvents()) + cmd.AddCommand(databaseIndex()) return cmd } @@ -2548,3 +2549,64 @@ func RunDatabaseEvents(c *CmdConfig) error { item := &displayers.DatabaseEvents{DatabaseEvents: dbEvents} return c.Display(item) } + +func databaseIndex() *Command { + cmd := &Command{ + Command: &cobra.Command{ + Use: "indexes", + Short: `Display commands to manage indexes for opensearch clusters`, + Long: `The subcommands under ` + "`" + `doctl databases indexes` + "`" + ` enable the management of indexes for opensearch clusters`, + }, + } + + indexListDetails := ` +This command lists the following details for each index in an opensearch cluster: + + - The Name of the index. + - The Status of the index. + - The Health of the index. + - The Number of Shards in the index. + - The Number of Replicas in the index. + - The Number of Documents in the index. + - The Size of the index. + ` + + CmdBuilder(cmd, RunDatabaseIndexList, "list ", "Retrieve a list of indexes for a given opensearch cluster", indexListDetails, Writer, displayerType(&displayers.DatabaseOpenSearchIndexes{}), aliasOpt("ls")) + cmdDatabaseIndexDelete := CmdBuilder(cmd, RunDatabaseIndexDelete, "delete ", "Deletes an opensearch index by index name", "", Writer, aliasOpt("rm")) + AddBoolFlag(cmdDatabaseIndexDelete, doctl.ArgForce, doctl.ArgShortForce, false, "Deletes the opensearch index without a confirmation prompt") + + return cmd +} + +func RunDatabaseIndexList(c *CmdConfig) error { + if len(c.Args) == 0 { + return doctl.NewMissingArgsErr(c.NS) + } + + databaseID := c.Args[0] + indexes, err := c.Databases().ListIndexes(databaseID) + if err != nil { + return err + } + item := &displayers.DatabaseOpenSearchIndexes{DatabaseIndexes: indexes} + return c.Display(item) +} + +func RunDatabaseIndexDelete(c *CmdConfig) error { + if len(c.Args) < 2 { + return doctl.NewMissingArgsErr(c.NS) + } + + force, err := c.Doit.GetBool(c.NS, doctl.ArgForce) + if err != nil { + return err + } + + if force || AskForConfirmDelete("opensearch index", 1) == nil { + databaseID := c.Args[0] + indexName := c.Args[1] + return c.Databases().DeleteIndex(databaseID, indexName) + } + + return errOperationAborted +} diff --git a/commands/databases_test.go b/commands/databases_test.go index ebe39ffc9..5a2c92afe 100644 --- a/commands/databases_test.go +++ b/commands/databases_test.go @@ -262,6 +262,7 @@ func TestDatabasesCommand(t *testing.T) { "sql-mode", "configuration", "topics", + "indexes", ) } diff --git a/commands/displayers/database.go b/commands/displayers/database.go index 51ccce77e..9e349ec42 100644 --- a/commands/displayers/database.go +++ b/commands/displayers/database.go @@ -1691,3 +1691,60 @@ func (dr *DatabaseEvents) KV() []map[string]any { } return out } + +type DatabaseOpenSearchIndexes struct { + DatabaseIndexes do.DatabaseIndexes +} + +var _ Displayable = &DatabaseOpenSearchIndexes{} + +func (dt *DatabaseOpenSearchIndexes) JSON(out io.Writer) error { + return writeJSON(dt.DatabaseIndexes, out) +} + +func (dt *DatabaseOpenSearchIndexes) Cols() []string { + return []string{ + "Index Name", + "Status", + "Health", + "Size", + "Docs", + "Create At", + "Number of Shards", + "Number of Replica", + } +} + +func (dt *DatabaseOpenSearchIndexes) ColMap() map[string]string { + + return map[string]string{ + "Index Name": "Index Name", + "Status": "Status", + "Health": "Health", + "Size": "Size", + "Docs": "Docs", + "Create At": "Create At", + "Number of Shards": "Number of Shards", + "Number of Replica": "Number of Replica", + } +} + +func (dt *DatabaseOpenSearchIndexes) KV() []map[string]any { + out := make([]map[string]any, 0, len(dt.DatabaseIndexes)) + + for _, t := range dt.DatabaseIndexes { + o := map[string]any{ + "Index Name": t.IndexName, + "Number of Shards": t.NumberofShards, + "Number of Replica": t.NumberofReplicas, + "Status": t.Status, + "Health": t.Health, + "Size": t.Size, + "Docs": t.Docs, + "Create At": t.CreateTime, + } + out = append(out, o) + } + + return out +} diff --git a/do/databases.go b/do/databases.go index cf5f9ae41..fe61b552e 100644 --- a/do/databases.go +++ b/do/databases.go @@ -133,6 +133,14 @@ type DatabaseEvent struct { // DatabaseEvents is a slice of DatabaseEvent type DatabaseEvents []DatabaseEvent +// DatabaseIndexes is a slice of DatabaseIndex +type DatabaseIndexes []DatabaseIndex + +// DatabaseIndex is a wrapper for godo.DatabaseIndex +type DatabaseIndex struct { + *godo.DatabaseIndex +} + // DatabasesService is an interface for interacting with DigitalOcean's Database API type DatabasesService interface { List() (Databases, error) @@ -194,6 +202,9 @@ type DatabasesService interface { DeleteTopic(string, string) error ListDatabaseEvents(string) (DatabaseEvents, error) + + ListIndexes(string) (DatabaseIndexes, error) + DeleteIndex(string, string) error } type databasesService struct { @@ -789,3 +800,37 @@ func (ds *databasesService) ListDatabaseEvents(databaseID string) (DatabaseEvent } return list, nil } + +func (ds *databasesService) ListIndexes(databaseID string) (DatabaseIndexes, error) { + f := func(opt *godo.ListOptions) ([]any, *godo.Response, error) { + list, resp, err := ds.client.Databases.ListIndexes(context.TODO(), databaseID, opt) + if err != nil { + return nil, nil, err + } + + si := make([]any, len(list)) + for i := range list { + si[i] = list[i] + } + + return si, resp, err + } + + si, err := PaginateResp(f) + if err != nil { + return nil, err + } + + list := make(DatabaseIndexes, len(si)) + for i := range si { + t := si[i].(godo.DatabaseIndex) + list[i] = DatabaseIndex{DatabaseIndex: &t} + } + return list, nil +} + +func (ds *databasesService) DeleteIndex(databaseID, indexName string) error { + _, err := ds.client.Databases.DeleteIndex(context.TODO(), databaseID, indexName) + + return err +} diff --git a/do/mocks/DatabasesService.go b/do/mocks/DatabasesService.go index 6ebecf288..bb63b7a47 100644 --- a/do/mocks/DatabasesService.go +++ b/do/mocks/DatabasesService.go @@ -158,6 +158,20 @@ func (mr *MockDatabasesServiceMockRecorder) DeleteDB(arg0, arg1 any) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteDB", reflect.TypeOf((*MockDatabasesService)(nil).DeleteDB), arg0, arg1) } +// DeleteIndex mocks base method. +func (m *MockDatabasesService) DeleteIndex(arg0, arg1 string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteIndex", arg0, arg1) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteIndex indicates an expected call of DeleteIndex. +func (mr *MockDatabasesServiceMockRecorder) DeleteIndex(arg0, arg1 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteIndex", reflect.TypeOf((*MockDatabasesService)(nil).DeleteIndex), arg0, arg1) +} + // DeletePool mocks base method. func (m *MockDatabasesService) DeletePool(arg0, arg1 string) error { m.ctrl.T.Helper() @@ -484,6 +498,21 @@ func (mr *MockDatabasesServiceMockRecorder) ListDatabaseEvents(arg0 any) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListDatabaseEvents", reflect.TypeOf((*MockDatabasesService)(nil).ListDatabaseEvents), arg0) } +// ListIndexes mocks base method. +func (m *MockDatabasesService) ListIndexes(arg0 string) (do.DatabaseIndexes, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListIndexes", arg0) + ret0, _ := ret[0].(do.DatabaseIndexes) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListIndexes indicates an expected call of ListIndexes. +func (mr *MockDatabasesServiceMockRecorder) ListIndexes(arg0 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListIndexes", reflect.TypeOf((*MockDatabasesService)(nil).ListIndexes), arg0) +} + // ListOptions mocks base method. func (m *MockDatabasesService) ListOptions() (*do.DatabaseOptions, error) { m.ctrl.T.Helper() diff --git a/go.mod b/go.mod index 79e3289bb..b1895f4be 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22 require ( github.com/blang/semver v3.5.1+incompatible github.com/creack/pty v1.1.21 - github.com/digitalocean/godo v1.118.0 + github.com/digitalocean/godo v1.121.0 github.com/docker/cli v24.0.5+incompatible github.com/docker/docker v25.0.6+incompatible github.com/docker/docker-credential-helpers v0.7.0 // indirect @@ -30,7 +30,7 @@ require ( github.com/stretchr/testify v1.8.4 golang.org/x/crypto v0.22.0 golang.org/x/net v0.24.0 // indirect - golang.org/x/oauth2 v0.19.0 + golang.org/x/oauth2 v0.22.0 golang.org/x/sys v0.20.0 // indirect gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.26.2 @@ -121,7 +121,7 @@ require ( go.opentelemetry.io/otel/trace v1.19.0 // indirect golang.org/x/mod v0.11.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.5.0 // indirect + golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.10.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.4 // indirect diff --git a/go.sum b/go.sum index b5b0aa4aa..978041c90 100644 --- a/go.sum +++ b/go.sum @@ -91,8 +91,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4= -github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo= +github.com/digitalocean/godo v1.121.0 h1:ilXiHuEnhbJs2fmFEPX0r/QQ6KfiOIMAhJN3f8NiCfI= +github.com/digitalocean/godo v1.121.0/go.mod h1:WQVH83OHUy6gC4gXpEVQKtxTd4L5oCp+5OialidkPLY= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= @@ -517,8 +517,8 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= -golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -599,8 +599,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/integration/database_opensearch_index_delete_test.go b/integration/database_opensearch_index_delete_test.go new file mode 100644 index 000000000..66f675549 --- /dev/null +++ b/integration/database_opensearch_index_delete_test.go @@ -0,0 +1,69 @@ +package integration + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/http/httputil" + "os/exec" + "strings" + "testing" + + "github.com/sclevine/spec" + "github.com/stretchr/testify/require" +) + +var _ = suite("database/index/delete", func(t *testing.T, when spec.G, it spec.S) { + var ( + expect *require.Assertions + server *httptest.Server + ) + + it.Before(func() { + expect = require.New(t) + + server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + switch req.URL.Path { + case "/v2/databases/some-database-id/indexes/some-index-id": + auth := req.Header.Get("Authorization") + if auth != "Bearer some-magic-token" { + w.WriteHeader(http.StatusTeapot) + } + + if req.Method != http.MethodDelete { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + w.WriteHeader(http.StatusNoContent) + default: + dump, err := httputil.DumpRequest(req, true) + if err != nil { + t.Fatal("failed to dump request") + } + + t.Fatalf("received unknown request: %s", dump) + } + })) + }) + + when("all required flags are passed", func() { + it("deletes the database", func() { + cmd := exec.Command(builtBinaryPath, + "-t", "some-magic-token", + "-u", server.URL, + "database", + "indexes", + "delete", + "some-database-id", + "some-index-id", + "-f", + ) + + output, err := cmd.CombinedOutput() + expect.NoError(err, fmt.Sprintf("received error output: %s", output)) + expect.Equal(strings.TrimSpace(""), strings.TrimSpace(string(output))) + expect.Empty(output) + }) + }) +}) diff --git a/integration/database_opensearch_index_list_test.go b/integration/database_opensearch_index_list_test.go new file mode 100644 index 000000000..8639b4917 --- /dev/null +++ b/integration/database_opensearch_index_list_test.go @@ -0,0 +1,88 @@ +package integration + +import ( + "fmt" + "net/http" + "net/http/httptest" + "net/http/httputil" + "os/exec" + "strings" + "testing" + + "github.com/sclevine/spec" + "github.com/stretchr/testify/require" +) + +var _ = suite("database/index/list", func(t *testing.T, when spec.G, it spec.S) { + var ( + expect *require.Assertions + server *httptest.Server + ) + + it.Before(func() { + expect = require.New(t) + + server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + switch req.URL.Path { + case "/v2/databases/some-database-id/indexes": + auth := req.Header.Get("Authorization") + if auth != "Bearer some-magic-token" { + w.WriteHeader(http.StatusTeapot) + } + + if req.Method != http.MethodGet { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + w.Write([]byte(databaseOpenSearchIndexListResponse)) + default: + dump, err := httputil.DumpRequest(req, true) + if err != nil { + t.Fatal("failed to dump request") + } + + t.Fatalf("received unknown request: %s", dump) + } + })) + }) + + when("all required flags are passed", func() { + it("lists indexes for an opensearch cluster", func() { + cmd := exec.Command(builtBinaryPath, + "-t", "some-magic-token", + "-u", server.URL, + "database", + "indexes", + "list", + "some-database-id", + ) + + output, err := cmd.CombinedOutput() + expect.NoError(err, fmt.Sprintf("received error output: %s", output)) + expect.Equal(strings.TrimSpace(databaseOpenSearchIndexListOutput), strings.TrimSpace(string(output))) + }) + }) +}) + +const ( + databaseOpenSearchIndexListOutput = ` +Index Name Status Health Size Docs Create At Number of Shards Number of Replica +sql-datasources open green 624 0 2024-05-24T07:44:45Z 1 0` + databaseOpenSearchIndexListResponse = ` +{ + "indexes": [ + { + "index_name": "sql-datasources", + "status": "open", + "health": "green", + "size": 624, + "docs": 0, + "create_time": "2024-05-24T07:44:45Z", + "number_of_shards": 1, + "number_of_replica": 0 + } + ] +} +` +) diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index 331dadb9a..7203f1ed7 100644 --- a/vendor/github.com/digitalocean/godo/CHANGELOG.md +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -1,5 +1,23 @@ # Change Log +## [v1.121.0] - 2024-08-20 + +- #715 - @danaelhe - Databases: Bring back Logsink Support +- #710 - @bhardwajRahul - Update GODO to include new Openseach index crud changes +- #712 - @danaelhe - Database: Namespace logsink +- #711 - @danaelhe - Databases: Add Logsinks CRUD support + +## [v1.120.0] - 2024-08-08 + +- #708 - @markusthoemmes - APPS-9201 Add `UpdateAllSourceVersions` parameter to update app calls +- #706 - @andrewsomething - database: Add Size to DatabaseReplica struct + +## [v1.119.0] - 2024-07-24 + +- #704 - @ElanHasson - APPS-9133 - Add support for OPENSEARCH as a database engine option +- #703 - @dependabot[bot] - Bump github.com/hashicorp/go-retryablehttp from 0.7.4 to 0.7.7 +- #699 - @ElanHasson - APPS-8790 Add support to App Platform Log Forwarding for an OpenSearch DBaaS cluster destination. + ## [v1.118.0] - 2024-06-04 **Note**: This release contains features in closed beta (#700). diff --git a/vendor/github.com/digitalocean/godo/apps.gen.go b/vendor/github.com/digitalocean/godo/apps.gen.go index f5be2992f..229705402 100644 --- a/vendor/github.com/digitalocean/godo/apps.gen.go +++ b/vendor/github.com/digitalocean/godo/apps.gen.go @@ -193,7 +193,7 @@ type AppBuildConfigCNBVersioning struct { // AppDatabaseSpec struct for AppDatabaseSpec type AppDatabaseSpec struct { - // The name. Must be unique across all components within the same app. + // The database's name. The name must be unique across all components within the same app and cannot use capital letters. Name string `json:"name"` Engine AppDatabaseSpecEngine `json:"engine,omitempty"` Version string `json:"version,omitempty"` @@ -216,12 +216,13 @@ type AppDatabaseSpecEngine string // List of AppDatabaseSpecEngine const ( - AppDatabaseSpecEngine_Unset AppDatabaseSpecEngine = "UNSET" - AppDatabaseSpecEngine_MySQL AppDatabaseSpecEngine = "MYSQL" - AppDatabaseSpecEngine_PG AppDatabaseSpecEngine = "PG" - AppDatabaseSpecEngine_Redis AppDatabaseSpecEngine = "REDIS" - AppDatabaseSpecEngine_MongoDB AppDatabaseSpecEngine = "MONGODB" - AppDatabaseSpecEngine_Kafka AppDatabaseSpecEngine = "KAFKA" + AppDatabaseSpecEngine_Unset AppDatabaseSpecEngine = "UNSET" + AppDatabaseSpecEngine_MySQL AppDatabaseSpecEngine = "MYSQL" + AppDatabaseSpecEngine_PG AppDatabaseSpecEngine = "PG" + AppDatabaseSpecEngine_Redis AppDatabaseSpecEngine = "REDIS" + AppDatabaseSpecEngine_MongoDB AppDatabaseSpecEngine = "MONGODB" + AppDatabaseSpecEngine_Kafka AppDatabaseSpecEngine = "KAFKA" + AppDatabaseSpecEngine_Opensearch AppDatabaseSpecEngine = "OPENSEARCH" ) // AppDedicatedIp Represents a dedicated egress ip. @@ -446,11 +447,13 @@ type AppLogDestinationSpecLogtail struct { // AppLogDestinationSpecOpenSearch OpenSearch configuration. type AppLogDestinationSpecOpenSearch struct { - // OpenSearch API Endpoint. Only HTTPS is supported. Format: https://:. - Endpoint string `json:"endpoint"` + // OpenSearch API Endpoint. Only HTTPS is supported. Format: https://:. Cannot be specified if `cluster_name` is also specified. + Endpoint string `json:"endpoint,omitempty"` BasicAuth *OpenSearchBasicAuth `json:"basic_auth,omitempty"` // The index name to use for the logs. If not set, the default index name is \"logs\". IndexName string `json:"index_name,omitempty"` + // The name of a DigitalOcean DBaaS OpenSearch cluster to use as a log forwarding destination. Cannot be specified if `endpoint` is also specified. + ClusterName string `json:"cluster_name,omitempty"` } // AppLogDestinationSpecPapertrail Papertrail configuration. @@ -1156,7 +1159,8 @@ type AppInstanceSize struct { // (Deprecated) The slug of the corresponding downgradable instance size on the lower tier. TierDowngradeTo string `json:"tier_downgrade_to,omitempty"` // Indicates if the tier instance size can enable autoscaling. - Scalable bool `json:"scalable,omitempty"` + Scalable bool `json:"scalable,omitempty"` + // (Deprecated) Indicates if the tier instance size is in feature preview state. FeaturePreview bool `json:"feature_preview,omitempty"` // Indicates if the tier instance size allows more than one instance. SingleInstanceOnly bool `json:"single_instance_only,omitempty"` @@ -1184,10 +1188,10 @@ type ListBuildpacksResponse struct { // OpenSearchBasicAuth Configure Username and/or Password for Basic authentication. type OpenSearchBasicAuth struct { - // Username to authenticate with. - User string `json:"user"` - // Password for user defined in User. - Password string `json:"password"` + // Username to authenticate with. Only required when `endpoint` is set. Defaults to `doadmin` when `cluster_name` is set. + User string `json:"user,omitempty"` + // Password for user defined in User. Is required when `endpoint` is set. Cannot be set if using a DigitalOcean DBaaS OpenSearch cluster. + Password string `json:"password,omitempty"` } // AppProposeRequest struct for AppProposeRequest diff --git a/vendor/github.com/digitalocean/godo/apps.go b/vendor/github.com/digitalocean/godo/apps.go index cd72f7408..ebf341c04 100644 --- a/vendor/github.com/digitalocean/godo/apps.go +++ b/vendor/github.com/digitalocean/godo/apps.go @@ -80,6 +80,8 @@ type AppLogs struct { // AppUpdateRequest represents a request to update an app. type AppUpdateRequest struct { Spec *AppSpec `json:"spec"` + // Whether or not to update the source versions (for example fetching a new commit or image digest) of all components. By default (when this is false) only newly added sources will be updated to avoid changes like updating the scale of a component from also updating the respective code. + UpdateAllSourceVersions bool `json:"update_all_source_versions"` } // DeploymentCreateRequest represents a request to create a deployment. diff --git a/vendor/github.com/digitalocean/godo/apps_accessors.go b/vendor/github.com/digitalocean/godo/apps_accessors.go index 0bbba2ddf..05c5ac4b1 100644 --- a/vendor/github.com/digitalocean/godo/apps_accessors.go +++ b/vendor/github.com/digitalocean/godo/apps_accessors.go @@ -1389,6 +1389,14 @@ func (a *AppLogDestinationSpecOpenSearch) GetBasicAuth() *OpenSearchBasicAuth { return a.BasicAuth } +// GetClusterName returns the ClusterName field. +func (a *AppLogDestinationSpecOpenSearch) GetClusterName() string { + if a == nil { + return "" + } + return a.ClusterName +} + // GetEndpoint returns the Endpoint field. func (a *AppLogDestinationSpecOpenSearch) GetEndpoint() string { if a == nil { diff --git a/vendor/github.com/digitalocean/godo/databases.go b/vendor/github.com/digitalocean/godo/databases.go index b915391c8..8c4065514 100644 --- a/vendor/github.com/digitalocean/godo/databases.go +++ b/vendor/github.com/digitalocean/godo/databases.go @@ -36,6 +36,10 @@ const ( databaseTopicsPath = databaseBasePath + "/%s/topics" databaseMetricsCredentialsPath = databaseBasePath + "/metrics/credentials" databaseEvents = databaseBasePath + "/%s/events" + databaseIndexesPath = databaseBasePath + "/%s/indexes" + databaseIndexPath = databaseBasePath + "/%s/indexes/%s" + databaseLogsinkPath = databaseBasePath + "/%s/logsink/%s" + databaseLogsinksPath = databaseBasePath + "/%s/logsink" ) // SQL Mode constants allow for MySQL-specific SQL flavor configuration. @@ -159,6 +163,13 @@ type DatabasesService interface { GetMetricsCredentials(context.Context) (*DatabaseMetricsCredentials, *Response, error) UpdateMetricsCredentials(context.Context, *DatabaseUpdateMetricsCredentialsRequest) (*Response, error) ListDatabaseEvents(context.Context, string, *ListOptions) ([]DatabaseEvent, *Response, error) + ListIndexes(context.Context, string, *ListOptions) ([]DatabaseIndex, *Response, error) + DeleteIndex(context.Context, string, string) (*Response, error) + CreateLogsink(ctx context.Context, databaseID string, createLogsink *DatabaseCreateLogsinkRequest) (*DatabaseLogsink, *Response, error) + GetLogsink(ctx context.Context, databaseID string, logsinkID string) (*DatabaseLogsink, *Response, error) + ListLogsinks(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseLogsink, *Response, error) + UpdateLogsink(ctx context.Context, databaseID string, logsinkID string, updateLogsink *DatabaseUpdateLogsinkRequest) (*Response, error) + DeleteLogsink(ctx context.Context, databaseID, logsinkID string) (*Response, error) } // DatabasesServiceOp handles communication with the Databases related methods @@ -323,6 +334,14 @@ type DatabaseTopic struct { Config *TopicConfig `json:"config,omitempty"` } +// DatabaseLogsink represents a logsink +type DatabaseLogsink struct { + ID string `json:"sink_id"` + Name string `json:"sink_name,omitempty"` + Type string `json:"sink_type,omitempty"` + Config *DatabaseLogsinkConfig `json:"config,omitempty"` +} + // TopicPartition represents the state of a Kafka topic partition type TopicPartition struct { EarliestOffset uint64 `json:"earliest_offset,omitempty"` @@ -392,6 +411,7 @@ type DatabaseReplica struct { PrivateNetworkUUID string `json:"private_network_uuid,omitempty"` Tags []string `json:"tags,omitempty"` StorageSizeMib uint64 `json:"storage_size_mib,omitempty"` + Size string `json:"size"` } // DatabasePool represents a database connection pool @@ -471,6 +491,35 @@ type DatabaseFirewallRule struct { CreatedAt time.Time `json:"created_at"` } +// DatabaseCreateLogsinkRequest is used to create logsink for a database cluster +type DatabaseCreateLogsinkRequest struct { + Name string `json:"sink_name"` + Type string `json:"sink_type"` + Config *DatabaseLogsinkConfig `json:"config"` +} + +// DatabaseUpdateLogsinkRequest is used to update logsink for a database cluster +type DatabaseUpdateLogsinkRequest struct { + Config *DatabaseLogsinkConfig `json:"config"` +} + +// DatabaseLogsinkConfig represents one of the configurable options (rsyslog_logsink, elasticsearch_logsink, or opensearch_logsink) for a logsink. +type DatabaseLogsinkConfig struct { + URL string `json:"url,omitempty"` + IndexPrefix string `json:"index_prefix,omitempty"` + IndexDaysMax string `json:"index_days_max,omitempty"` + Timeout string `json:"timeout,omitempty"` + Server string `json:"server,omitempty"` + Port int `json:"port,omitempty"` + TLS bool `json:"tls,omitempty"` + Format string `json:"format,omitempty"` + Logline string `json:"logline,omitempty"` + SD string `json:"sd,omitempty"` + CA string `json:"ca,omitempty"` + Key string `json:"key,omitempty"` + Cert string `json:"cert,omitempty"` +} + // PostgreSQLConfig holds advanced configurations for PostgreSQL database clusters. type PostgreSQLConfig struct { AutovacuumFreezeMaxAge *int `json:"autovacuum_freeze_max_age,omitempty"` @@ -679,6 +728,10 @@ type databaseTopicsRoot struct { Topics []DatabaseTopic `json:"topics"` } +type databaseLogsinksRoot struct { + Sinks []DatabaseLogsink `json:"sinks"` +} + type databaseMetricsCredentialsRoot struct { Credentials *DatabaseMetricsCredentials `json:"credentials"` } @@ -732,6 +785,28 @@ type ListDatabaseEventsRoot struct { Events []DatabaseEvent `json:"events"` } +type DatabaseIndex struct { + IndexName string `json:"index_name"` + NumberofShards uint64 `json:"number_of_shards"` + NumberofReplicas uint64 `json:"number_of_replicas"` + Size int64 `json:"size,omitempty"` + Health string `json:"health,omitempty"` + Status string `json:"status,omitempty"` + Docs int64 `json:"docs,omitempty"` + CreateTime string `json:"create_time"` + Replication *IndexReplication `json:"replication,omitempty"` +} + +type IndexReplication struct { + LeaderIndex string `json:"leader_index,omitempty"` + LeaderProject string `json:"leader_project,omitempty"` + LeaderService string `json:"leader_service,omitempty"` +} + +type databaseIndexesRoot struct { + Indexes []DatabaseIndex `json:"indexes"` +} + // URN returns a URN identifier for the database func (d Database) URN() string { return ToURN("dbaas", d.ID) @@ -1559,3 +1634,116 @@ func (svc *DatabasesServiceOp) ListDatabaseEvents(ctx context.Context, databaseI return root.Events, resp, nil } + +// ListIndexes returns all indexes for a given opensearch cluster +func (svc *DatabasesServiceOp) ListIndexes(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseIndex, *Response, error) { + path := fmt.Sprintf(databaseIndexesPath, databaseID) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } + req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + root := new(databaseIndexesRoot) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Indexes, resp, nil +} + +// DeleteIndex will delete an existing opensearch index +func (svc *DatabasesServiceOp) DeleteIndex(ctx context.Context, databaseID, name string) (*Response, error) { + path := fmt.Sprintf(databaseIndexPath, databaseID, name) + req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil) + if err != nil { + return nil, err + } + resp, err := svc.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + +// CreateLogsink creates a new logsink for a database +func (svc *DatabasesServiceOp) CreateLogsink(ctx context.Context, databaseID string, createLogsink *DatabaseCreateLogsinkRequest) (*DatabaseLogsink, *Response, error) { + path := fmt.Sprintf(databaseLogsinksPath, databaseID) + req, err := svc.client.NewRequest(ctx, http.MethodPost, path, createLogsink) + if err != nil { + return nil, nil, err + } + + root := new(DatabaseLogsink) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root, resp, nil +} + +// GetLogsink gets a logsink for a database +func (svc *DatabasesServiceOp) GetLogsink(ctx context.Context, databaseID string, logsinkID string) (*DatabaseLogsink, *Response, error) { + path := fmt.Sprintf(databaseLogsinkPath, databaseID, logsinkID) + req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + root := new(DatabaseLogsink) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root, resp, nil +} + +// ListTopics returns all topics for a given kafka cluster +func (svc *DatabasesServiceOp) ListLogsinks(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseLogsink, *Response, error) { + path := fmt.Sprintf(databaseLogsinksPath, databaseID) + path, err := addOptions(path, opts) + if err != nil { + return nil, nil, err + } + req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + root := new(databaseLogsinksRoot) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Sinks, resp, nil +} + +// UpdateLogsink updates a logsink for a database cluster +func (svc *DatabasesServiceOp) UpdateLogsink(ctx context.Context, databaseID string, logsinkID string, updateLogsink *DatabaseUpdateLogsinkRequest) (*Response, error) { + path := fmt.Sprintf(databaseLogsinkPath, databaseID, logsinkID) + req, err := svc.client.NewRequest(ctx, http.MethodPut, path, updateLogsink) + if err != nil { + return nil, err + } + + resp, err := svc.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + return resp, nil +} + +// DeleteLogsink deletes a logsink for a database cluster +func (svc *DatabasesServiceOp) DeleteLogsink(ctx context.Context, databaseID, logsinkID string) (*Response, error) { + path := fmt.Sprintf(databaseLogsinkPath, databaseID, logsinkID) + req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil) + if err != nil { + return nil, err + } + resp, err := svc.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + return resp, nil +} diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index 6d69ece72..9362e1185 100644 --- a/vendor/github.com/digitalocean/godo/godo.go +++ b/vendor/github.com/digitalocean/godo/godo.go @@ -21,7 +21,7 @@ import ( ) const ( - libraryVersion = "1.118.0" + libraryVersion = "1.121.0" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" diff --git a/vendor/golang.org/x/oauth2/LICENSE b/vendor/golang.org/x/oauth2/LICENSE index 6a66aea5e..2a7cf70da 100644 --- a/vendor/golang.org/x/oauth2/LICENSE +++ b/vendor/golang.org/x/oauth2/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go index 90a2c3d6d..09f6a49b8 100644 --- a/vendor/golang.org/x/oauth2/oauth2.go +++ b/vendor/golang.org/x/oauth2/oauth2.go @@ -393,7 +393,7 @@ func ReuseTokenSource(t *Token, src TokenSource) TokenSource { } } -// ReuseTokenSource returns a TokenSource that acts in the same manner as the +// ReuseTokenSourceWithExpiry returns a TokenSource that acts in the same manner as the // TokenSource returned by ReuseTokenSource, except the expiry buffer is // configurable. The expiration time of a token is calculated as // t.Expiry.Add(-earlyExpiry). diff --git a/vendor/golang.org/x/time/LICENSE b/vendor/golang.org/x/time/LICENSE index 6a66aea5e..2a7cf70da 100644 --- a/vendor/golang.org/x/time/LICENSE +++ b/vendor/golang.org/x/time/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/modules.txt b/vendor/modules.txt index 2a3908e4e..c1aa57163 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -61,7 +61,7 @@ github.com/creack/pty # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew -# github.com/digitalocean/godo v1.118.0 +# github.com/digitalocean/godo v1.121.0 ## explicit; go 1.20 github.com/digitalocean/godo github.com/digitalocean/godo/metrics @@ -439,7 +439,7 @@ golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/internal/socks golang.org/x/net/proxy -# golang.org/x/oauth2 v0.19.0 +# golang.org/x/oauth2 v0.22.0 ## explicit; go 1.18 golang.org/x/oauth2 golang.org/x/oauth2/internal @@ -459,7 +459,7 @@ golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm -# golang.org/x/time v0.5.0 +# golang.org/x/time v0.6.0 ## explicit; go 1.18 golang.org/x/time/rate # golang.org/x/tools v0.10.0