diff --git a/args.go b/args.go index ff56b1cea..ab5c34b57 100644 --- a/args.go +++ b/args.go @@ -406,6 +406,8 @@ const ( ArgDatabasePrivateConnectionBool = "private" // ArgDatabaseUserKafkaACLs will specify permissions on topics in kafka clsuter ArgDatabaseUserKafkaACLs = "acl" + // ArgDatabaseUserOpenSearchACLs will specify permissions on indexes in opensearch clsuter + ArgDatabaseUserOpenSearchACLs = "opensearch-acl" // ArgDatabaseTopicReplicationFactor is the replication factor of a kafka topic ArgDatabaseTopicReplicationFactor = "replication-factor" diff --git a/commands/databases.go b/commands/databases.go index 18ca9a586..4550de6c7 100644 --- a/commands/databases.go +++ b/commands/databases.go @@ -667,6 +667,7 @@ Database user accounts are scoped to one database cluster, to which they have fu }, } databaseKafkaACLsTxt := `A comma-separated list of kafka ACL rules, in ` + "`" + `topic:permission` + "`" + ` format.` + databaseOpenSearchACLsTxt := `A comma-separated list of OpenSearch ACL rules, in ` + "`" + `index:permission` + "`" + ` format.` userDetailsDesc := ` - The username for the user @@ -695,6 +696,7 @@ To retrieve a list of your databases and their IDs, call `+"`"+`doctl databases AddStringFlag(cmdDatabaseUserCreate, doctl.ArgDatabaseUserMySQLAuthPlugin, "", "", "Sets authorization plugin for a MySQL user. Possible values: `caching_sha2_password` or `mysql_native_password`") AddStringSliceFlag(cmdDatabaseUserCreate, doctl.ArgDatabaseUserKafkaACLs, "", []string{}, databaseKafkaACLsTxt) + AddStringSliceFlag(cmdDatabaseUserCreate, doctl.ArgDatabaseUserOpenSearchACLs, "", []string{}, databaseOpenSearchACLsTxt) cmdDatabaseUserCreate.Example = `The following example creates a new user with the username ` + "`" + `example-user` + "`" + ` for a database cluster with the ID ` + "`" + `ca9f591d-f38h-5555-a0ef-1c02d1d1e35` + "`" + `: doctl databases user create ca9f591d-f38h-5555-a0ef-1c02d1d1e35 example-user` cmdDatabaseUserResetAuth := CmdBuilder(cmd, RunDatabaseUserResetAuth, "reset ", @@ -781,6 +783,17 @@ func RunDatabaseUserCreate(c *CmdConfig) error { } } + openSearchACLs, err := buildDatabaseCreateOpenSearchUserACLs(c) + if err != nil { + return err + } + + if len(openSearchACLs) != 0 { + req.Settings = &godo.DatabaseUserSettings{ + OpenSearchACL: openSearchACLs, + } + } + user, err := c.Databases().CreateUser(databaseID, req) if err != nil { return err @@ -809,6 +822,26 @@ func buildDatabaseCreateKafkaUserACls(c *CmdConfig) (kafkaACls []*godo.KafkaACL, return kafkaACls, nil } +func buildDatabaseCreateOpenSearchUserACLs(c *CmdConfig) (openSearchACLs []*godo.OpenSearchACL, err error) { + acls, err := c.Doit.GetStringSlice(c.NS, doctl.ArgDatabaseUserOpenSearchACLs) + if err != nil { + return nil, err + } + for _, acl := range acls { + pair := strings.SplitN(acl, ":", 2) + if len(pair) != 2 { + return nil, fmt.Errorf("unexpected input value [%v], must be a index:permission pair", pair) + } + + openSearchACL := new(godo.OpenSearchACL) + openSearchACL.Index = pair[0] + openSearchACL.Permission = pair[1] + + openSearchACLs = append(openSearchACLs, openSearchACL) + } + return openSearchACLs, nil +} + func RunDatabaseUserResetAuth(c *CmdConfig) error { if len(c.Args) < 2 { return doctl.NewMissingArgsErr(c.NS) diff --git a/commands/databases_test.go b/commands/databases_test.go index 5a2c92afe..f2f94a35f 100644 --- a/commands/databases_test.go +++ b/commands/databases_test.go @@ -962,6 +962,29 @@ func TestDatabaseUserCreate(t *testing.T) { assert.NoError(t, err) }) + // Successful call with kafka acl set + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + r := &godo.DatabaseCreateUserRequest{ + Name: testDBUser.Name, + Settings: &godo.DatabaseUserSettings{ + OpenSearchACL: []*godo.OpenSearchACL{ + { + Permission: "admin", + Index: "test", + }, + }, + }, + } + + tm.databases.EXPECT().CreateUser(testDBCluster.ID, r).Return(&testDBUser, nil) + + config.Args = append(config.Args, testDBCluster.ID, testDBUser.Name) + config.Doit.Set(config.NS, doctl.ArgDatabaseUserOpenSearchACLs, "test:admin") + + err := RunDatabaseUserCreate(config) + assert.NoError(t, err) + }) + // Error withTestClient(t, func(config *CmdConfig, tm *tcMocks) { tm.databases.EXPECT().CreateUser( diff --git a/go.mod b/go.mod index b1895f4be..bc85ef226 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.121.0 + github.com/digitalocean/godo v1.123.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.22.0 + golang.org/x/oauth2 v0.23.0 golang.org/x/sys v0.20.0 // indirect gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.26.2 diff --git a/go.sum b/go.sum index 978041c90..4e68d2ab6 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.121.0 h1:ilXiHuEnhbJs2fmFEPX0r/QQ6KfiOIMAhJN3f8NiCfI= -github.com/digitalocean/godo v1.121.0/go.mod h1:WQVH83OHUy6gC4gXpEVQKtxTd4L5oCp+5OialidkPLY= +github.com/digitalocean/godo v1.123.0 h1:EowFmnVevXIKn9svPDTz0NK4+f+eE3v5easKD9hjc1k= +github.com/digitalocean/godo v1.123.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.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.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= diff --git a/integration/database_user_create_test.go b/integration/database_user_create_test.go index f983f8bb4..8b703dc46 100644 --- a/integration/database_user_create_test.go +++ b/integration/database_user_create_test.go @@ -105,6 +105,25 @@ var _ = suite("database/user/create", func(t *testing.T, when spec.G, it spec.S) expect.Equal(strings.TrimSpace(databaseUserCreateOutput), strings.TrimSpace(string(output))) }) }) + + when("the opensearch acl flag is present", func() { + it("creates the database user", func() { + cmd := exec.Command(builtBinaryPath, + "-t", "some-magic-token", + "-u", server.URL, + "database", + "user", + "create", + "some-database-id", + "some-user-name", + "--opensearch-acl", "log-*:read", + ) + + output, err := cmd.CombinedOutput() + expect.NoError(err, fmt.Sprintf("received error output: %s", output)) + expect.Equal(strings.TrimSpace(databaseUserCreateOutput), strings.TrimSpace(string(output))) + }) + }) }) const ( @@ -118,7 +137,15 @@ some-user-name normal jge5lfxtzhx42iff "name": "{{.Name}}", "role": "normal", "password": "jge5lfxtzhx42iff", - "mysql_settings": { "auth_plugin": "mysql_native_password" } + "mysql_settings": { "auth_plugin": "mysql_native_password" }, + "settings": { + "opensearch_acl": [ + { + "permission": "read", + "index": "log-*" + } + ] + } } } ` diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index 7203f1ed7..fabeb100f 100644 --- a/vendor/github.com/digitalocean/godo/CHANGELOG.md +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -1,5 +1,14 @@ # Change Log +## [v1.123.0] - 2024-09-06 + +- #719 - @andrewsomething - apps: mark ListTiers and GetTier as deprecated + +## [v1.122.0] - 2024-09-04 + +- #717 - @danaelhe - DB: Fix Logsink Attribute Types +- #716 - @bhardwajRahul - Databases: Add support for OpenSearch ACL + ## [v1.121.0] - 2024-08-20 - #715 - @danaelhe - Databases: Bring back Logsink Support diff --git a/vendor/github.com/digitalocean/godo/apps.go b/vendor/github.com/digitalocean/godo/apps.go index ebf341c04..ac792658e 100644 --- a/vendor/github.com/digitalocean/godo/apps.go +++ b/vendor/github.com/digitalocean/godo/apps.go @@ -384,6 +384,9 @@ func (s *AppsServiceOp) ListRegions(ctx context.Context) ([]*AppRegion, *Respons } // ListTiers lists available app tiers. +// +// Deprecated: The '/v2/apps/tiers' endpoint has been deprecated as app tiers +// are no longer tied to instance sizes. The concept of tiers is being retired. func (s *AppsServiceOp) ListTiers(ctx context.Context) ([]*AppTier, *Response, error) { path := fmt.Sprintf("%s/tiers", appsBasePath) req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) @@ -399,6 +402,9 @@ func (s *AppsServiceOp) ListTiers(ctx context.Context) ([]*AppTier, *Response, e } // GetTier retrieves information about a specific app tier. +// +// Deprecated: The '/v2/apps/tiers/{slug}' endpoints have been deprecated as app +// tiers are no longer tied to instance sizes. The concept of tiers is being retired. func (s *AppsServiceOp) GetTier(ctx context.Context, slug string) (*AppTier, *Response, error) { path := fmt.Sprintf("%s/tiers/%s", appsBasePath, slug) req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil) diff --git a/vendor/github.com/digitalocean/godo/databases.go b/vendor/github.com/digitalocean/godo/databases.go index 8c4065514..3b6869cfe 100644 --- a/vendor/github.com/digitalocean/godo/databases.go +++ b/vendor/github.com/digitalocean/godo/databases.go @@ -252,9 +252,16 @@ type KafkaACL struct { Topic string `json:"topic,omitempty"` } -// DatabaseUserSettings contains Kafka-specific user settings +// OpenSearchACL contains OpenSearch specific user access control information +type OpenSearchACL struct { + Permission string `json:"permission,omitempty"` + Index string `json:"index,omitempty"` +} + +// DatabaseUserSettings contains user settings type DatabaseUserSettings struct { - ACL []*KafkaACL `json:"acl,omitempty"` + ACL []*KafkaACL `json:"acl,omitempty"` + OpenSearchACL []*OpenSearchACL `json:"opensearch_acl,omitempty"` } // DatabaseMySQLUserSettings contains MySQL-specific user settings @@ -505,19 +512,19 @@ type DatabaseUpdateLogsinkRequest struct { // 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"` + URL string `json:"url,omitempty"` + IndexPrefix string `json:"index_prefix,omitempty"` + IndexDaysMax int `json:"index_days_max,omitempty"` + Timeout float32 `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. diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index 9362e1185..f28dab88e 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.121.0" + libraryVersion = "1.123.0" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" diff --git a/vendor/golang.org/x/oauth2/token.go b/vendor/golang.org/x/oauth2/token.go index 5bbb33217..109997d77 100644 --- a/vendor/golang.org/x/oauth2/token.go +++ b/vendor/golang.org/x/oauth2/token.go @@ -49,6 +49,13 @@ type Token struct { // mechanisms for that TokenSource will not be used. Expiry time.Time `json:"expiry,omitempty"` + // ExpiresIn is the OAuth2 wire format "expires_in" field, + // which specifies how many seconds later the token expires, + // relative to an unknown time base approximately around "now". + // It is the application's responsibility to populate + // `Expiry` from `ExpiresIn` when required. + ExpiresIn int64 `json:"expires_in,omitempty"` + // raw optionally contains extra metadata from the server // when updating a token. raw interface{} diff --git a/vendor/modules.txt b/vendor/modules.txt index c1aa57163..68a1ed468 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.121.0 +# github.com/digitalocean/godo v1.123.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.22.0 +# golang.org/x/oauth2 v0.23.0 ## explicit; go 1.18 golang.org/x/oauth2 golang.org/x/oauth2/internal