mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-01-11 16:38:15 -05:00
feat(tools/alloydb-create-instance): Add new custom tool kind for alloydb create-instance (#1379)
## Description
---
This pull request introduces a new custom tool kind
`alloydb-create-instance` that allows users to create a new AlloyDB
instance within a specified cluster.
### Example Configuration
```yaml
tools:
create_instance:
kind: alloydb-create-instance
source: my-alloydb-admin-source
description: Use this tool to create a new AlloyDB instance within a specified cluster.
```
### Example Request
```
curl -X POST http://127.0.0.1:5000/api/tool/create_instance/invoke \
-H "Content-Type: application/json" \
-d '{
"project": "example-project",
"location": "us-central1",
"cluster": "example-cluster",
"instance": "example-instance-id",
"instanceType": "PRIMARY",
"displayName": "example-instance"
}'
```
## PR Checklist
---
> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:
- [x] Make sure you reviewed
[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a
[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
before writing your code! That way we can discuss the change, evaluate
designs, and agree on the general idea
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [x] Make sure to add `!` if this involve a breaking change
🛠️ Fixes #<issue_number_goes_here>
This commit is contained in:
@@ -43,6 +43,7 @@ import (
|
||||
|
||||
// Import tool packages for side effect of registration
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreatecluster"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateinstance"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateuser"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbgetcluster"
|
||||
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbgetinstance"
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
---
|
||||
title: "alloydb-create-cluster"
|
||||
title: alloydb-create-cluster
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
The "alloydb-create-cluster" tool creates a new AlloyDB for PostgreSQL cluster in a specified project and location.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-create-cluster
|
||||
description: "The \"alloydb-create-cluster\" tool creates a new AlloyDB for PostgreSQL cluster in a specified project and location.\n"
|
||||
aliases: [/resources/tools/alloydb-create-cluster]
|
||||
---
|
||||
|
||||
## About
|
||||
@@ -15,20 +13,25 @@ This tool provisions a cluster with a **private IP address** within the specifie
|
||||
|
||||
**Permissions & APIs Required:**
|
||||
Before using, ensure the following on your GCP project:
|
||||
1. The [AlloyDB API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) is enabled.
|
||||
2. The user or service account executing the tool has the following IAM roles:
|
||||
- `roles/alloydb.admin`: To create and manage the AlloyDB cluster.
|
||||
|
||||
1. The [AlloyDB API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) is enabled.
|
||||
2. The user or service account executing the tool has one of the following IAM roles:
|
||||
|
||||
|
||||
- `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role)
|
||||
- `roles/owner` (the Owner basic IAM role)
|
||||
- `roles/editor` (the Editor basic IAM role)
|
||||
|
||||
The tool takes the following input parameters:
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `project` | string | The GCP project ID where the cluster will be created. | Yes |
|
||||
| `location` | string | The GCP location where the cluster will be created. Default: `us-central1`. If quota is exhausted then use other regions. | No |
|
||||
| `cluster` | string | A unique identifier for the new AlloyDB cluster. | Yes |
|
||||
| `password` | string | A secure password for the initial user. | Yes |
|
||||
| `network` | string | The name of the VPC network to connect the cluster to. Default: `default`. | No |
|
||||
| `user` | string | The name for the initial superuser. Default: `postgres`. | No |
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :------------------------------------------------------------------------------------------------------------------------ | :------- |
|
||||
| `project` | string | The GCP project ID where the cluster will be created. | Yes |
|
||||
| `cluster` | string | A unique identifier for the new AlloyDB cluster. | Yes |
|
||||
| `password` | string | A secure password for the initial user. | Yes |
|
||||
| `location` | string | The GCP location where the cluster will be created. Default: `us-central1`. If quota is exhausted then use other regions. | No |
|
||||
| `network` | string | The name of the VPC network to connect the cluster to. Default: `default`. | No |
|
||||
| `user` | string | The name for the initial superuser. Default: `postgres`. | No |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -39,9 +42,11 @@ tools:
|
||||
source: alloydb-admin-source
|
||||
description: Use this tool to create a new AlloyDB cluster in a given project and location.
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-create-cluster. | |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** | |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- | - |
|
||||
| kind | string | true | Must be alloydb-create-cluster. | |
|
||||
| source | string | true | The name of an `alloydb-admin` source. | |
|
||||
| description | string | false | Description of the tool that is passed to the agent. | |
|
||||
|
||||
56
docs/en/resources/tools/alloydb/alloydb-create-instance.md
Normal file
56
docs/en/resources/tools/alloydb/alloydb-create-instance.md
Normal file
@@ -0,0 +1,56 @@
|
||||
---
|
||||
title: alloydb-create-instance
|
||||
type: docs
|
||||
weight: 1
|
||||
description: "The \"alloydb-create-instance\" tool creates a new AlloyDB instance within a specified cluster.\n"
|
||||
aliases: [/resources/tools/alloydb-create-instance]
|
||||
---
|
||||
|
||||
## About
|
||||
|
||||
The `alloydb-create-instance` tool creates a new AlloyDB instance (PRIMARY or READ_POOL) within a specified cluster. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
This tool provisions a new instance with a **public IP address**.
|
||||
|
||||
**Permissions & APIs Required:**
|
||||
Before using, ensure the following on your GCP project:
|
||||
|
||||
1. The [AlloyDB API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) is enabled.
|
||||
2. The user or service account executing the tool has one of the following IAM roles:
|
||||
|
||||
|
||||
- `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role)
|
||||
- `roles/owner` (the Owner basic IAM role)
|
||||
- `roles/editor` (the Editor basic IAM role)
|
||||
|
||||
The tool takes the following input parameters:
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :------------- | :----- | :------------------------------------------------------------------------------------------------ | :------- |
|
||||
| `project` | string | The GCP project ID where the cluster exists. | Yes |
|
||||
| `location` | string | The GCP location where the cluster exists (e.g., `us-central1`). | Yes |
|
||||
| `cluster` | string | The ID of the existing cluster to add this instance to. | Yes |
|
||||
| `instance` | string | A unique identifier for the new AlloyDB instance. | Yes |
|
||||
| `instanceType` | string | The type of instance. Valid values are: `PRIMARY` and `READ_POOL`. Default: `PRIMARY` | No |
|
||||
| `displayName` | string | An optional, user-friendly name for the instance. | No |
|
||||
| `nodeCount` | int | The number of nodes for a read pool. Required only if `instanceType` is `READ_POOL`. Default: `1` | No |
|
||||
|
||||
> Note
|
||||
> The tool sets the `password.enforce_complexity` database flag to `on`, requiring new database passwords to meet complexity rules.
|
||||
|
||||
## Example
|
||||
|
||||
```yaml
|
||||
tools:
|
||||
create_instance:
|
||||
kind: alloydb-create-instance
|
||||
source: alloydb-admin-source
|
||||
description: Use this tool to create a new AlloyDB instance within a specified cluster.
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-create-instance. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
@@ -1,11 +1,9 @@
|
||||
---
|
||||
title: "alloydb-create-user"
|
||||
title: alloydb-create-user
|
||||
type: docs
|
||||
weight: 2
|
||||
description: >
|
||||
The "alloydb-create-user" tool creates a new database user within a specified AlloyDB cluster.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-create-user
|
||||
weight: 2
|
||||
description: "The \"alloydb-create-user\" tool creates a new database user within a specified AlloyDB cluster.\n"
|
||||
aliases: [/resources/tools/alloydb-create-user]
|
||||
---
|
||||
|
||||
## About
|
||||
@@ -14,21 +12,24 @@ The `alloydb-create-user` tool creates a new database user (`ALLOYDB_BUILT_IN` o
|
||||
|
||||
**Permissions & APIs Required:**
|
||||
Before using, ensure the following on your GCP project:
|
||||
|
||||
1. The [AlloyDB API](https://console.cloud.google.com/apis/library/alloydb.googleapis.com) is enabled.
|
||||
2. The user or service account executing the tool has the following IAM roles:
|
||||
- `roles/alloydb.admin`: To create and manage AlloyDB users.
|
||||
2. The user or service account executing the tool has one of the following IAM roles:
|
||||
- `roles/alloydb.admin` (the AlloyDB Admin predefined IAM role)
|
||||
- `roles/owner` (the Owner basic IAM role)
|
||||
- `roles/editor` (the Editor basic IAM role)
|
||||
|
||||
The tool takes the following input parameters:
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| `project` | string | The GCP project ID where the cluster exists. | Yes |
|
||||
| `cluster` | string | The ID of the existing cluster where the user will be created. | Yes |
|
||||
| `location` | string | The GCP location where the cluster exists (e.g., `us-central1`). | Yes |
|
||||
| `user` | string | The name for the new user. Must be unique within the cluster. | Yes |
|
||||
| `userType` | string | The type of user. Valid values: `ALLOYDB_BUILT_IN` and `ALLOYDB_IAM_USER`. `ALLOYDB_IAM_USER` is recommended. | Yes |
|
||||
| `password` | string | A secure password for the user. Required only if `userType` is `ALLOYDB_BUILT_IN`. | No |
|
||||
| `databaseRoles` | array(string) | Optional. A list of database roles to grant to the new user (e.g., `pg_read_all_data`). | No |
|
||||
| Parameter | Type | Description | Required |
|
||||
| :-------------- | :------------ | :------------------------------------------------------------------------------------------------------------ | :------- |
|
||||
| `project` | string | The GCP project ID where the cluster exists. | Yes |
|
||||
| `cluster` | string | The ID of the existing cluster where the user will be created. | Yes |
|
||||
| `location` | string | The GCP location where the cluster exists (e.g., `us-central1`). | Yes |
|
||||
| `user` | string | The name for the new user. Must be unique within the cluster. | Yes |
|
||||
| `userType` | string | The type of user. Valid values: `ALLOYDB_BUILT_IN` and `ALLOYDB_IAM_USER`. `ALLOYDB_IAM_USER` is recommended. | Yes |
|
||||
| `password` | string | A secure password for the user. Required only if `userType` is `ALLOYDB_BUILT_IN`. | No |
|
||||
| `databaseRoles` | array(string) | Optional. A list of database roles to grant to the new user (e.g., `pg_read_all_data`). | No |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -39,9 +40,11 @@ tools:
|
||||
source: alloydb-admin-source
|
||||
description: Use this tool to create a new database user for an AlloyDB cluster.
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-create-user. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-create-user. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
---
|
||||
title: "alloydb-get-cluster"
|
||||
title: alloydb-get-cluster
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
The "alloydb-get-cluster" tool retrieves details for a specific AlloyDB cluster.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-get-cluster
|
||||
description: "The \"alloydb-get-cluster\" tool retrieves details for a specific AlloyDB cluster.\n"
|
||||
aliases: [/resources/tools/alloydb-get-cluster]
|
||||
---
|
||||
|
||||
## About
|
||||
|
||||
The `alloydb-get-cluster` tool retrieves detailed information for a single, specified AlloyDB cluster. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :--------------------------------------------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to get cluster for. | Yes |
|
||||
| `location` | string | The location of the cluster (e.g., 'us-central1'). | Yes |
|
||||
| `cluster` | string | The ID of the cluster to retrieve. | Yes |
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to get cluster for. | Yes |
|
||||
| `location` | string | The location of the cluster (e.g., 'us-central1'). | Yes |
|
||||
| `cluster` | string | The ID of the cluster to retrieve. | Yes |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -27,9 +25,11 @@ tools:
|
||||
source: my-alloydb-admin-source
|
||||
description: Use this tool to retrieve details for a specific AlloyDB cluster.
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-get-cluster. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-get-cluster. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
---
|
||||
title: "alloydb-get-instance"
|
||||
title: alloydb-get-instance
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
The "alloydb-get-instance" tool retrieves details for a specific AlloyDB instance.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-get-instance
|
||||
description: "The \"alloydb-get-instance\" tool retrieves details for a specific AlloyDB instance.\n"
|
||||
aliases: [/resources/tools/alloydb-get-instance]
|
||||
---
|
||||
|
||||
## About
|
||||
|
||||
The `alloydb-get-instance` tool retrieves detailed information for a single, specified AlloyDB instance. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :--------------------------------------------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to get instance for. | Yes |
|
||||
| `location` | string | The location of the instance (e.g., 'us-central1'). | Yes |
|
||||
| `cluster` | string | The ID of the cluster. | Yes |
|
||||
| `instance` | string | The ID of the instance to retrieve. | Yes |
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :-------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to get instance for. | Yes |
|
||||
| `location` | string | The location of the instance (e.g., 'us-central1'). | Yes |
|
||||
| `cluster` | string | The ID of the cluster. | Yes |
|
||||
| `instance` | string | The ID of the instance to retrieve. | Yes |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -28,9 +26,11 @@ tools:
|
||||
source: my-alloydb-admin-source
|
||||
description: Use this tool to retrieve details for a specific AlloyDB instance.
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-get-instance. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-get-instance. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
---
|
||||
title: "alloydb-get-user"
|
||||
title: alloydb-get-user
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
The "alloydb-get-user" tool retrieves details for a specific AlloyDB user.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-get-user
|
||||
description: "The \"alloydb-get-user\" tool retrieves details for a specific AlloyDB user.\n"
|
||||
aliases: [/resources/tools/alloydb-get-user]
|
||||
---
|
||||
|
||||
## About
|
||||
|
||||
The `alloydb-get-user` tool retrieves detailed information for a single, specified AlloyDB user. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :--------------------------------------------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to get user for. | Yes |
|
||||
| `location` | string | The location of the cluster (e.g., 'us-central1'). | Yes |
|
||||
| `cluster` | string | The ID of the cluster to retrieve the user from. | Yes |
|
||||
| `user` | string | The ID of the user to retrieve. | Yes |
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to get user for. | Yes |
|
||||
| `location` | string | The location of the cluster (e.g., 'us-central1'). | Yes |
|
||||
| `cluster` | string | The ID of the cluster to retrieve the user from. | Yes |
|
||||
| `user` | string | The ID of the user to retrieve. | Yes |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -28,9 +26,11 @@ tools:
|
||||
source: my-alloydb-admin-source
|
||||
description: Use this tool to retrieve details for a specific AlloyDB user.
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-get-user. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-get-user. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
---
|
||||
title: "alloydb-list-clusters"
|
||||
title: alloydb-list-clusters
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
The "alloydb-list-clusters" tool lists the AlloyDB clusters in a given project and location.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-list-clusters
|
||||
description: "The \"alloydb-list-clusters\" tool lists the AlloyDB clusters in a given project and location.\n"
|
||||
aliases: [/resources/tools/alloydb-list-clusters]
|
||||
---
|
||||
|
||||
## About
|
||||
@@ -13,7 +11,7 @@ aliases:
|
||||
The `alloydb-list-clusters` tool retrieves AlloyDB cluster information for all or specified locations in a given project. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
|
||||
`alloydb-list-clusters` tool lists the detailed information of AlloyDB cluster(cluster name, state, configuration, etc) for a given project and location. The tool takes the following input parameters:
|
||||
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :----------------------------------------------------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to list clusters for. | Yes |
|
||||
@@ -28,9 +26,11 @@ tools:
|
||||
source: alloydb-admin-source
|
||||
description: Use this tool to list all AlloyDB clusters in a given project and location.
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-list-clusters. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-list-clusters. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
---
|
||||
title: "alloydb-list-instances"
|
||||
title: alloydb-list-instances
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
The "alloydb-list-instances" tool lists the AlloyDB instances for a given project, cluster and location.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-list-instances
|
||||
description: "The \"alloydb-list-instances\" tool lists the AlloyDB instances for a given project, cluster and location.\n"
|
||||
aliases: [/resources/tools/alloydb-list-instances]
|
||||
---
|
||||
|
||||
## About
|
||||
@@ -13,7 +11,7 @@ aliases:
|
||||
The `alloydb-list-instances` tool retrieves AlloyDB instance information for all or specified clusters and locations in a given project. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
|
||||
`alloydb-list-instances` tool lists the detailed information of AlloyDB instances (instance name, type, IP address, state, configuration, etc) for a given project, cluster and location. The tool takes the following input parameters:
|
||||
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :--------------------------------------------------------------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to list instances for. | Yes |
|
||||
@@ -29,9 +27,11 @@ tools:
|
||||
source: alloydb-admin-source
|
||||
description: Use this tool to list all AlloyDB instances for a given project, cluster and location.
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-list-instances. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-list-instances. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
---
|
||||
title: "alloydb-list-users"
|
||||
title: alloydb-list-users
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
The "alloydb-list-users" tool lists all database users within an AlloyDB cluster.
|
||||
aliases:
|
||||
- /resources/tools/alloydb-list-users
|
||||
description: "The \"alloydb-list-users\" tool lists all database users within an AlloyDB cluster.\n"
|
||||
aliases: [/resources/tools/alloydb-list-users]
|
||||
---
|
||||
|
||||
## About
|
||||
|
||||
The `alloydb-list-users` tool lists all database users within an AlloyDB cluster. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
The tool takes the following input parameters:
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :--------------------------------------------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to list users for. | Yes |
|
||||
| `cluster` | string | The ID of the cluster to list users from. | Yes |
|
||||
| `location` | string | The location of the cluster (e.g., 'us-central1'). | Yes |
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID to list users for. | Yes |
|
||||
| `cluster` | string | The ID of the cluster to list users from. | Yes |
|
||||
| `location` | string | The location of the cluster (e.g., 'us-central1'). | Yes |
|
||||
|
||||
## Example
|
||||
|
||||
@@ -28,9 +26,11 @@ tools:
|
||||
source: alloydb-admin-source
|
||||
description: Use this tool to list all database users within an AlloyDB cluster
|
||||
```
|
||||
|
||||
## Reference
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|-------------|:------------------------------------------:|:------------:|--------------------------------------------------------------------------------------------------|
|
||||
| kind | string | true | Must be alloydb-list-users. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------- |
|
||||
| kind | string | true | Must be alloydb-list-users. |
|
||||
| source | string | true | The name of an `alloydb-admin` source. |
|
||||
| description | string | false | Description of the tool that is passed to the agent. |
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
---
|
||||
title: "alloydb-wait-for-operation"
|
||||
title: alloydb-wait-for-operation
|
||||
type: docs
|
||||
weight: 10
|
||||
description: >
|
||||
Wait for a long-running AlloyDB operation to complete.
|
||||
description: "Wait for a long-running AlloyDB operation to complete.\n"
|
||||
---
|
||||
|
||||
The `alloydb-wait-for-operation` tool is a utility tool that waits for a
|
||||
@@ -11,11 +10,11 @@ long-running AlloyDB operation to complete. It does this by polling the AlloyDB
|
||||
Admin API operation status endpoint until the operation is finished, using
|
||||
exponential backoff. It is compatible with [alloydb-admin](../../sources/alloydb-admin.md) source.
|
||||
|
||||
| Parameter | Type | Description | Required |
|
||||
| :--------- | :----- | :--------------------------------------------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID. | Yes |
|
||||
| `location` | string | The location of the operation (e.g., 'us-central1'). | Yes |
|
||||
| `operation`| string | The ID of the operation to wait for. | Yes |
|
||||
| Parameter | Type | Description | Required |
|
||||
| :---------- | :----- | :--------------------------------------------------- | :------- |
|
||||
| `project` | string | The GCP project ID. | Yes |
|
||||
| `location` | string | The location of the operation (e.g., 'us-central1'). | Yes |
|
||||
| `operation` | string | The ID of the operation to wait for. | Yes |
|
||||
|
||||
{{< notice info >}}
|
||||
This tool is intended for developer assistant workflows with human-in-the-loop
|
||||
@@ -41,9 +40,9 @@ tools:
|
||||
| **field** | **type** | **required** | **description** |
|
||||
| ----------- | :------: | :----------: | ---------------------------------------------------------------------------------------------------------------- |
|
||||
| kind | string | true | Must be "alloydb-wait-for-operation". |
|
||||
| source | string | true | The name of a `alloydb-admin` source to use for authentication. |
|
||||
| description | string | false | A description of the tool. |
|
||||
| delay | duration | false | The initial delay between polling requests (e.g., `3s`). Defaults to 3 seconds. |
|
||||
| maxDelay | duration | false | The maximum delay between polling requests (e.g., `4m`). Defaults to 4 minutes. |
|
||||
| multiplier | float | false | The multiplier for the polling delay. The delay is multiplied by this value after each request. Defaults to 2.0. |
|
||||
| maxRetries | int | false | The maximum number of polling attempts before giving up. Defaults to 10. |
|
||||
| source | string | true | The name of a `alloydb-admin` source to use for authentication. |
|
||||
| description | string | false | A description of the tool. |
|
||||
| delay | duration | false | The initial delay between polling requests (e.g., `3s`). Defaults to 3 seconds. |
|
||||
| maxDelay | duration | false | The maximum delay between polling requests (e.g., `4m`). Defaults to 4 minutes. |
|
||||
| multiplier | float | false | The multiplier for the polling delay. The delay is multiplied by this value after each request. Defaults to 2.0. |
|
||||
| maxRetries | int | false | The maximum number of polling attempts before giving up. Defaults to 10. |
|
||||
|
||||
@@ -13,114 +13,43 @@
|
||||
# limitations under the License.
|
||||
|
||||
sources:
|
||||
alloydb-api-source:
|
||||
kind: http
|
||||
baseUrl: https://alloydb.googleapis.com
|
||||
headers:
|
||||
Authorization: Bearer ${API_KEY}
|
||||
Content-Type: application/json
|
||||
alloydb-admin-source:
|
||||
kind: alloydb-admin
|
||||
tools:
|
||||
create_cluster:
|
||||
kind: alloydb-create-cluster
|
||||
source: alloydb-admin-source
|
||||
description: "Create a new AlloyDB cluster. This is a long-running operation, but the API call returns quickly. This will return operation id to be used by get operations tool. Take all parameters from user in one go."
|
||||
wait_for_operation:
|
||||
kind: alloydb-wait-for-operation
|
||||
source: alloydb-admin-source
|
||||
description: "This will poll on operations API until the operation is done. For checking operation status we need projectId, locationID and operationId. Once instance is created give follow up steps on how to use the variables to bring data plane MCP server up in local and remote setup."
|
||||
delay: 1s
|
||||
maxDelay: 4m
|
||||
multiplier: 2
|
||||
maxRetries: 10
|
||||
create_instance:
|
||||
kind: http
|
||||
source: alloydb-api-source
|
||||
method: POST
|
||||
path: /v1/projects/{{.projectId}}/locations/{{.locationId}}/clusters/{{.clusterId}}/instances
|
||||
description: "Creates a new AlloyDB instance (PRIMARY, READ_POOL, or SECONDARY) within a cluster. This is a long-running operation. Take all parameters from user in one go. This will return operation id to be used by get operations tool."
|
||||
pathParams:
|
||||
- name: projectId
|
||||
type: string
|
||||
description: "The GCP project ID."
|
||||
- name: locationId
|
||||
type: string
|
||||
description: "The location of the cluster (e.g., 'us-central1')."
|
||||
default: us-central1
|
||||
- name: clusterId
|
||||
type: string
|
||||
description: "The ID of the cluster to create the instance in."
|
||||
queryParams:
|
||||
- name: instanceId
|
||||
type: string
|
||||
description: "A unique ID for the new AlloyDB instance."
|
||||
requestBody: |
|
||||
{
|
||||
"instanceType": "{{.instanceType}}",
|
||||
{{- if .displayName }}
|
||||
"displayName": "{{.displayName}}",
|
||||
{{- end }}
|
||||
{{- if eq .instanceType "READ_POOL" }}
|
||||
"readPoolConfig": {
|
||||
"nodeCount": {{.nodeCount}}
|
||||
},
|
||||
{{- end }}
|
||||
{{- if eq .instanceType "SECONDARY" }}
|
||||
"secondaryConfig": {
|
||||
"primaryClusterName": "{{.primaryClusterName}}"
|
||||
},
|
||||
{{- end }}
|
||||
"networkConfig": {
|
||||
"enablePublicIp": true
|
||||
},
|
||||
"databaseFlags": {
|
||||
"password.enforce_complexity": "on"
|
||||
}
|
||||
}
|
||||
bodyParams:
|
||||
- name: instanceType
|
||||
type: string
|
||||
description: "The type of instance to create. Required. Valid values are: PRIMARY, READ_POOL, SECONDARY."
|
||||
- name: displayName
|
||||
type: string
|
||||
description: "An optional, user-friendly name for the instance."
|
||||
- name: nodeCount
|
||||
type: integer
|
||||
description: "The number of nodes in the read pool. Required only if instanceType is READ_POOL. Default is 1."
|
||||
default: 1
|
||||
- name: primaryClusterName
|
||||
type: string
|
||||
description: "The full resource name of the primary cluster for a SECONDARY instance. Required only if instanceType is SECONDARY. Otherwise don't ask"
|
||||
default: ""
|
||||
kind: alloydb-create-instance
|
||||
source: alloydb-admin-source
|
||||
list_clusters:
|
||||
kind: alloydb-list-clusters
|
||||
source: alloydb-admin-source
|
||||
description: "Lists all AlloyDB clusters in a given project and location."
|
||||
list_instances:
|
||||
kind: alloydb-list-instances
|
||||
source: alloydb-admin-source
|
||||
description: "Lists all AlloyDB instances within a specific cluster."
|
||||
list_users:
|
||||
kind: alloydb-list-users
|
||||
source: alloydb-admin-source
|
||||
description: "Lists all database users within a specific AlloyDB cluster."
|
||||
create_user:
|
||||
kind: alloydb-create-user
|
||||
source: alloydb-admin-source
|
||||
description: "Creates a new database user in an AlloyDB cluster. Takes the new user's name and a secure password. Optionally, a list of database roles can be assigned. Always ask the user for the type of user to create. ALLOYDB_IAM_USER is recommended."
|
||||
get_cluster:
|
||||
kind: alloydb-get-cluster
|
||||
source: alloydb-admin-source
|
||||
description: "Retrieves details of a specific AlloyDB cluster."
|
||||
get_instance:
|
||||
kind: alloydb-get-instance
|
||||
source: alloydb-admin-source
|
||||
description: "Retrieves details of a specific AlloyDB instance."
|
||||
get_user:
|
||||
kind: alloydb-get-user
|
||||
source: alloydb-admin-source
|
||||
description: "Retrieves details of a specific AlloyDB user."
|
||||
|
||||
toolsets:
|
||||
alloydb_postgres_admin_tools:
|
||||
|
||||
@@ -85,7 +85,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
|
||||
|
||||
description := cfg.Description
|
||||
if description == "" {
|
||||
description = "Creates a new AlloyDB cluster. This is a long-running operation, but the API call returns quickly. This will return operation id to be used by get operations tool."
|
||||
description = "Creates a new AlloyDB cluster. This is a long-running operation, but the API call returns quickly. This will return operation id to be used by get operations tool. Take all parameters from user in one go."
|
||||
}
|
||||
|
||||
mcpManifest := tools.McpManifest{
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package alloydbcreateinstance
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/googleapis/genai-toolbox/internal/sources"
|
||||
alloydbadmin "github.com/googleapis/genai-toolbox/internal/sources/alloydbadmin"
|
||||
"github.com/googleapis/genai-toolbox/internal/tools"
|
||||
"google.golang.org/api/alloydb/v1"
|
||||
)
|
||||
|
||||
const kind string = "alloydb-create-instance"
|
||||
|
||||
func init() {
|
||||
if !tools.Register(kind, newConfig) {
|
||||
panic(fmt.Sprintf("tool kind %q already registered", kind))
|
||||
}
|
||||
}
|
||||
|
||||
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.ToolConfig, error) {
|
||||
actual := Config{Name: name}
|
||||
if err := decoder.DecodeContext(ctx, &actual); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return actual, nil
|
||||
}
|
||||
|
||||
// Configuration for the create-instance tool.
|
||||
type Config struct {
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Kind string `yaml:"kind" validate:"required"`
|
||||
Source string `yaml:"source" validate:"required"`
|
||||
Description string `yaml:"description"`
|
||||
AuthRequired []string `yaml:"authRequired"`
|
||||
}
|
||||
|
||||
// validate interface
|
||||
var _ tools.ToolConfig = Config{}
|
||||
|
||||
// ToolConfigKind returns the kind of the tool.
|
||||
func (cfg Config) ToolConfigKind() string {
|
||||
return kind
|
||||
}
|
||||
|
||||
// Initialize initializes the tool from the configuration.
|
||||
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
|
||||
rawS, ok := srcs[cfg.Source]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("source %q not found", cfg.Source)
|
||||
}
|
||||
|
||||
s, ok := rawS.(*alloydbadmin.Source)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid source for %q tool: source kind must be `alloydb-admin`", kind)
|
||||
}
|
||||
|
||||
allParameters := tools.Parameters{
|
||||
tools.NewStringParameter("project", "The GCP project ID."),
|
||||
tools.NewStringParameter("location", "The location of the cluster (e.g., 'us-central1')."),
|
||||
tools.NewStringParameter("cluster", "The ID of the cluster to create the instance in."),
|
||||
tools.NewStringParameter("instance", "A unique ID for the new AlloyDB instance."),
|
||||
tools.NewStringParameterWithDefault("instanceType", "PRIMARY", "The type of instance to create. Valid values are: PRIMARY and READ_POOL. Default is PRIMARY"),
|
||||
tools.NewStringParameterWithRequired("displayName", "An optional, user-friendly name for the instance.", false),
|
||||
tools.NewIntParameterWithDefault("nodeCount", 1, "The number of nodes in the read pool. Required only if instanceType is READ_POOL. Default is 1."),
|
||||
}
|
||||
paramManifest := allParameters.Manifest()
|
||||
|
||||
inputSchema := allParameters.McpManifest()
|
||||
inputSchema.Required = []string{"project", "location", "cluster", "instance"}
|
||||
|
||||
description := cfg.Description
|
||||
if description == "" {
|
||||
description = "Creates a new AlloyDB instance (PRIMARY or READ_POOL) within a cluster. This is a long-running operation. This will return operation id to be used by get operations tool. Take all parameters from user in one go."
|
||||
}
|
||||
|
||||
mcpManifest := tools.McpManifest{
|
||||
Name: cfg.Name,
|
||||
Description: description,
|
||||
InputSchema: inputSchema,
|
||||
}
|
||||
|
||||
return Tool{
|
||||
Name: cfg.Name,
|
||||
Kind: kind,
|
||||
Source: s,
|
||||
AllParams: allParameters,
|
||||
manifest: tools.Manifest{Description: description, Parameters: paramManifest, AuthRequired: cfg.AuthRequired},
|
||||
mcpManifest: mcpManifest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Tool represents the create-instance tool.
|
||||
type Tool struct {
|
||||
Name string `yaml:"name"`
|
||||
Kind string `yaml:"kind"`
|
||||
Description string `yaml:"description"`
|
||||
|
||||
Source *alloydbadmin.Source
|
||||
AllParams tools.Parameters `yaml:"allParams"`
|
||||
|
||||
manifest tools.Manifest
|
||||
mcpManifest tools.McpManifest
|
||||
}
|
||||
|
||||
// Invoke executes the tool's logic.
|
||||
func (t Tool) Invoke(ctx context.Context, params tools.ParamValues, accessToken tools.AccessToken) (any, error) {
|
||||
paramsMap := params.AsMap()
|
||||
project, ok := paramsMap["project"].(string)
|
||||
if !ok || project == "" {
|
||||
return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a non-empty string")
|
||||
}
|
||||
|
||||
location, ok := paramsMap["location"].(string)
|
||||
if !ok || location == "" {
|
||||
return nil, fmt.Errorf("invalid or missing 'location' parameter; expected a non-empty string")
|
||||
}
|
||||
|
||||
cluster, ok := paramsMap["cluster"].(string)
|
||||
if !ok || cluster == "" {
|
||||
return nil, fmt.Errorf("invalid or missing 'cluster' parameter; expected a non-empty string")
|
||||
}
|
||||
|
||||
instanceID, ok := paramsMap["instance"].(string)
|
||||
if !ok || instanceID == "" {
|
||||
return nil, fmt.Errorf("invalid or missing 'instance' parameter; expected a non-empty string")
|
||||
}
|
||||
|
||||
instanceType, ok := paramsMap["instanceType"].(string)
|
||||
if !ok || (instanceType != "READ_POOL" && instanceType != "PRIMARY") {
|
||||
return nil, fmt.Errorf("invalid 'instanceType' parameter; expected 'PRIMARY' or 'READ_POOL'")
|
||||
}
|
||||
|
||||
service, err := t.Source.GetService(ctx, string(accessToken))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
urlString := fmt.Sprintf("projects/%s/locations/%s/clusters/%s", project, location, cluster)
|
||||
|
||||
// Build the request body using the type-safe Instance struct.
|
||||
instance := &alloydb.Instance{
|
||||
InstanceType: instanceType,
|
||||
NetworkConfig: &alloydb.InstanceNetworkConfig{
|
||||
EnablePublicIp: true,
|
||||
},
|
||||
DatabaseFlags: map[string]string{
|
||||
"password.enforce_complexity": "on",
|
||||
},
|
||||
}
|
||||
|
||||
if displayName, ok := paramsMap["displayName"].(string); ok && displayName != "" {
|
||||
instance.DisplayName = displayName
|
||||
}
|
||||
|
||||
if instanceType == "READ_POOL" {
|
||||
nodeCount, ok := paramsMap["nodeCount"].(int)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid 'nodeCount' parameter; expected an integer for READ_POOL")
|
||||
}
|
||||
instance.ReadPoolConfig = &alloydb.ReadPoolConfig{
|
||||
NodeCount: int64(nodeCount),
|
||||
}
|
||||
}
|
||||
|
||||
// The Create API returns a long-running operation.
|
||||
resp, err := service.Projects.Locations.Clusters.Instances.Create(urlString, instance).InstanceId(instanceID).Do()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating AlloyDB instance: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ParseParams parses the parameters for the tool.
|
||||
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (tools.ParamValues, error) {
|
||||
return tools.ParseParams(t.AllParams, data, claims)
|
||||
}
|
||||
|
||||
// Manifest returns the tool's manifest.
|
||||
func (t Tool) Manifest() tools.Manifest {
|
||||
return t.manifest
|
||||
}
|
||||
|
||||
// McpManifest returns the tool's MCP manifest.
|
||||
func (t Tool) McpManifest() tools.McpManifest {
|
||||
return t.mcpManifest
|
||||
}
|
||||
|
||||
// Authorized checks if the tool is authorized.
|
||||
func (t Tool) Authorized(verifiedAuthServices []string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t Tool) RequiresClientAuthorization() bool {
|
||||
return t.Source.UseClientAuthorization()
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright 2025 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package alloydbcreateinstance_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
yaml "github.com/goccy/go-yaml"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/googleapis/genai-toolbox/internal/server"
|
||||
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||
alloydbcreateinstance "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateinstance"
|
||||
)
|
||||
|
||||
func TestParseFromYaml(t *testing.T) {
|
||||
ctx, err := testutils.ContextWithNewLogger()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
tcs := []struct {
|
||||
desc string
|
||||
in string
|
||||
want server.ToolConfigs
|
||||
}{
|
||||
{
|
||||
desc: "basic example",
|
||||
in: `
|
||||
tools:
|
||||
create-my-instance:
|
||||
kind: alloydb-create-instance
|
||||
source: my-alloydb-admin-source
|
||||
description: some description
|
||||
`,
|
||||
want: server.ToolConfigs{
|
||||
"create-my-instance": alloydbcreateinstance.Config{
|
||||
Name: "create-my-instance",
|
||||
Kind: "alloydb-create-instance",
|
||||
Source: "my-alloydb-admin-source",
|
||||
Description: "some description",
|
||||
AuthRequired: []string{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with auth required",
|
||||
in: `
|
||||
tools:
|
||||
create-my-instance-auth:
|
||||
kind: alloydb-create-instance
|
||||
source: my-alloydb-admin-source
|
||||
description: some description
|
||||
authRequired:
|
||||
- my-google-auth-service
|
||||
- other-auth-service
|
||||
`,
|
||||
want: server.ToolConfigs{
|
||||
"create-my-instance-auth": alloydbcreateinstance.Config{
|
||||
Name: "create-my-instance-auth",
|
||||
Kind: "alloydb-create-instance",
|
||||
Source: "my-alloydb-admin-source",
|
||||
Description: "some description",
|
||||
AuthRequired: []string{"my-google-auth-service", "other-auth-service"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
got := struct {
|
||||
Tools server.ToolConfigs `yaml:"tools"`
|
||||
}{}
|
||||
// Parse contents
|
||||
err := yaml.UnmarshalContext(ctx, testutils.FormatYaml(tc.in), &got)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to unmarshal: %s", err)
|
||||
}
|
||||
if diff := cmp.Diff(tc.want, got.Tools); diff != "" {
|
||||
t.Fatalf("incorrect parse: diff %v", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
|
||||
paramManifest := allParameters.Manifest()
|
||||
|
||||
inputSchema := allParameters.McpManifest()
|
||||
inputSchema.Required = []string{"project", "location"}
|
||||
inputSchema.Required = []string{"project"}
|
||||
|
||||
description := cfg.Description
|
||||
if description == "" {
|
||||
|
||||
@@ -35,16 +35,15 @@ import (
|
||||
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
|
||||
"github.com/googleapis/genai-toolbox/internal/testutils"
|
||||
"github.com/googleapis/genai-toolbox/tests"
|
||||
|
||||
)
|
||||
|
||||
var (
|
||||
AlloyDBCreateClusterToolKind = "alloydb-create-cluster"
|
||||
AlloyDBCreateUserToolKind = "alloydb-create-user"
|
||||
AlloyDBProject = os.Getenv("ALLOYDB_PROJECT")
|
||||
AlloyDBLocation = os.Getenv("ALLOYDB_REGION")
|
||||
AlloyDBCluster = os.Getenv("ALLOYDB_CLUSTER")
|
||||
AlloyDBInstance = os.Getenv("ALLOYDB_INSTANCE")
|
||||
AlloyDBUser = os.Getenv("ALLOYDB_POSTGRES_USER")
|
||||
AlloyDBProject = os.Getenv("ALLOYDB_PROJECT")
|
||||
AlloyDBLocation = os.Getenv("ALLOYDB_REGION")
|
||||
AlloyDBCluster = os.Getenv("ALLOYDB_CLUSTER")
|
||||
AlloyDBInstance = os.Getenv("ALLOYDB_INSTANCE")
|
||||
AlloyDBUser = os.Getenv("ALLOYDB_POSTGRES_USER")
|
||||
)
|
||||
|
||||
func getAlloyDBVars(t *testing.T) map[string]string {
|
||||
@@ -129,6 +128,21 @@ func getAlloyDBToolsConfig() map[string]any {
|
||||
"source": "alloydb-admin-source",
|
||||
"description": "Retrieves details of a specific AlloyDB user.",
|
||||
},
|
||||
"alloydb-create-cluster": map[string]any{
|
||||
"kind": "alloydb-create-cluster",
|
||||
"description": "create cluster",
|
||||
"source": "alloydb-admin-source",
|
||||
},
|
||||
"alloydb-create-instance": map[string]any{
|
||||
"kind": "alloydb-create-instance",
|
||||
"description": "create instance",
|
||||
"source": "alloydb-admin-source",
|
||||
},
|
||||
"alloydb-create-user": map[string]any{
|
||||
"kind": "alloydb-create-user",
|
||||
"description": "create user",
|
||||
"source": "alloydb-admin-source",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -160,8 +174,8 @@ func TestAlloyDBToolEndpoints(t *testing.T) {
|
||||
|
||||
// Run tool-specific invoke tests
|
||||
runAlloyDBListClustersTest(t, vars)
|
||||
runAlloyDBListUsersTest(t, vars)
|
||||
runAlloyDBListInstancesTest(t, vars)
|
||||
runAlloyDBListUsersTest(t, vars)
|
||||
runAlloyDBGetClusterTest(t, vars)
|
||||
runAlloyDBGetInstanceTest(t, vars)
|
||||
runAlloyDBGetUserTest(t, vars)
|
||||
@@ -375,40 +389,40 @@ func runAlloyDBListClustersTest(t *testing.T, vars map[string]string) {
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "list clusters for all locations",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "-"}`, vars["project"])),
|
||||
want: wantForAllLocations,
|
||||
name: "list clusters for all locations",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "-"}`, vars["project"])),
|
||||
want: wantForAllLocations,
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "list clusters specific location",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "us-central1"}`, vars["project"])),
|
||||
want: wantForSpecificLocation,
|
||||
name: "list clusters specific location",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "us-central1"}`, vars["project"])),
|
||||
want: wantForSpecificLocation,
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "list clusters missing project",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"location": "%s"}`, vars["location"])),
|
||||
name: "list clusters missing project",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"location": "%s"}`, vars["location"])),
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "list clusters non-existent location",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "abcd"}`, vars["project"])),
|
||||
name: "list clusters non-existent location",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "abcd"}`, vars["project"])),
|
||||
wantStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
name: "list clusters non-existent project",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "non-existent-project", "location": "%s"}`, vars["location"])),
|
||||
name: "list clusters non-existent project",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "non-existent-project", "location": "%s"}`, vars["location"])),
|
||||
wantStatusCode: http.StatusInternalServerError,
|
||||
},
|
||||
{
|
||||
name: "list clusters empty project",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "", "location": "%s"}`, vars["location"])),
|
||||
name: "list clusters empty project",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "", "location": "%s"}`, vars["location"])),
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "list clusters empty location",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": ""}`, vars["project"])),
|
||||
name: "list clusters empty location",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": ""}`, vars["project"])),
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
@@ -707,12 +721,12 @@ func runAlloyDBGetClusterTest(t *testing.T, vars map[string]string) {
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "get cluster success",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "%s", "cluster": "%s"}`, vars["project"], vars["location"], vars["cluster"])),
|
||||
want: map[string]any{
|
||||
"clusterType": "PRIMARY",
|
||||
"name": fmt.Sprintf("projects/%s/locations/%s/clusters/%s", vars["project"], vars["location"], vars["cluster"]),
|
||||
},
|
||||
name: "get cluster success",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "%s", "cluster": "%s"}`, vars["project"], vars["location"], vars["cluster"])),
|
||||
want: map[string]any{
|
||||
"clusterType": "PRIMARY",
|
||||
"name": fmt.Sprintf("projects/%s/locations/%s/clusters/%s", vars["project"], vars["location"], vars["cluster"]),
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
@@ -797,12 +811,12 @@ func runAlloyDBGetInstanceTest(t *testing.T, vars map[string]string) {
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "get instance success",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "%s", "cluster": "%s", "instance": "%s"}`, vars["project"], vars["location"], vars["cluster"], vars["instance"])),
|
||||
want: map[string]any{
|
||||
"instanceType": "PRIMARY",
|
||||
"name": fmt.Sprintf("projects/%s/locations/%s/clusters/%s/instances/%s", vars["project"], vars["location"], vars["cluster"], vars["instance"]),
|
||||
},
|
||||
name: "get instance success",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "%s", "cluster": "%s", "instance": "%s"}`, vars["project"], vars["location"], vars["cluster"], vars["instance"])),
|
||||
want: map[string]any{
|
||||
"instanceType": "PRIMARY",
|
||||
"name": fmt.Sprintf("projects/%s/locations/%s/clusters/%s/instances/%s", vars["project"], vars["location"], vars["cluster"], vars["instance"]),
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
@@ -892,12 +906,12 @@ func runAlloyDBGetUserTest(t *testing.T, vars map[string]string) {
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "get user success",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "%s", "cluster": "%s", "user": "%s"}`, vars["project"], vars["location"], vars["cluster"], vars["user"])),
|
||||
want: map[string]any{
|
||||
"name": fmt.Sprintf("projects/%s/locations/%s/clusters/%s/users/%s", vars["project"], vars["location"], vars["cluster"], vars["user"]),
|
||||
"userType": "ALLOYDB_BUILT_IN",
|
||||
},
|
||||
name: "get user success",
|
||||
requestBody: bytes.NewBufferString(fmt.Sprintf(`{"project": "%s", "location": "%s", "cluster": "%s", "user": "%s"}`, vars["project"], vars["location"], vars["cluster"], vars["user"])),
|
||||
want: map[string]any{
|
||||
"name": fmt.Sprintf("projects/%s/locations/%s/clusters/%s/users/%s", vars["project"], vars["location"], vars["cluster"], vars["user"]),
|
||||
"userType": "ALLOYDB_BUILT_IN",
|
||||
},
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
@@ -1016,9 +1030,24 @@ func (h *mockAlloyDBHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
case "c2-api-failure":
|
||||
response = `{"error":{"message":"internal api error"}}`
|
||||
statusCode = http.StatusInternalServerError
|
||||
case "i1-success":
|
||||
response = `{
|
||||
"metadata": {
|
||||
"@type": "type.googleapis.com/google.cloud.alloydb.v1.OperationMetadata",
|
||||
"target": "projects/p1/locations/l1/clusters/c1/instances/i1-success",
|
||||
"verb": "create",
|
||||
"requestedCancellation": false,
|
||||
"apiVersion": "v1"
|
||||
},
|
||||
"name": "projects/p1/locations/l1/operations/mock-operation-success"
|
||||
}`
|
||||
statusCode = http.StatusOK
|
||||
case "i2-api-failure":
|
||||
response = `{"error":{"message":"internal api error"}}`
|
||||
statusCode = http.StatusInternalServerError
|
||||
case "u1-iam-success":
|
||||
response = `{
|
||||
"databaseRoles": ["alloydbiamuser", "alloydbsuperuser"],
|
||||
"databaseRoles": ["alloydbiamuser"],
|
||||
"name": "projects/p1/locations/l1/clusters/c1/users/u1-iam-success",
|
||||
"userType": "ALLOYDB_IAM_USER"
|
||||
}`
|
||||
@@ -1077,7 +1106,7 @@ func TestAlloyDBCreateCluster(t *testing.T) {
|
||||
defer cancel()
|
||||
|
||||
var args []string
|
||||
toolsFile := getAlloyDBCreateToolsConfig()
|
||||
toolsFile := getAlloyDBToolsConfig()
|
||||
cmd, cleanupCmd, err := tests.StartCmd(ctx, toolsFile, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("command initialization returned an error: %v", err)
|
||||
@@ -1099,33 +1128,33 @@ func TestAlloyDBCreateCluster(t *testing.T) {
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "successful creation",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1-success", "password": "p1"}`,
|
||||
want: `{"name":"projects/p1/locations/l1/operations/mock-operation-success", "metadata": {"verb": "create", "target": "projects/p1/locations/l1/clusters/c1-success"}}`,
|
||||
name: "successful creation",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1-success", "password": "p1"}`,
|
||||
want: `{"name":"projects/p1/locations/l1/operations/mock-operation-success", "metadata": {"verb": "create", "target": "projects/p1/locations/l1/clusters/c1-success"}}`,
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "api failure",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c2-api-failure", "password": "p1"}`,
|
||||
want: "internal api error",
|
||||
name: "api failure",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c2-api-failure", "password": "p1"}`,
|
||||
want: "internal api error",
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing project",
|
||||
body: `{"location": "l1", "cluster": "c1", "password": "p1"}`,
|
||||
want: `parameter \"project\" is required`,
|
||||
name: "missing project",
|
||||
body: `{"location": "l1", "cluster": "c1", "password": "p1"}`,
|
||||
want: `parameter \"project\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing cluster",
|
||||
body: `{"project": "p1", "location": "l1", "password": "p1"}`,
|
||||
want: `parameter \"cluster\" is required`,
|
||||
name: "missing cluster",
|
||||
body: `{"project": "p1", "location": "l1", "password": "p1"}`,
|
||||
want: `parameter \"cluster\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing password",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1"}`,
|
||||
want: `parameter \"password\" is required`,
|
||||
name: "missing password",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1"}`,
|
||||
want: `parameter \"password\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
@@ -1179,15 +1208,15 @@ func TestAlloyDBCreateCluster(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlloyDBCreateUser(t *testing.T) {
|
||||
cleanup := setupTestServer(t, "userId")
|
||||
func TestAlloyDBCreateInstance(t *testing.T) {
|
||||
cleanup := setupTestServer(t, "instanceId")
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
var args []string
|
||||
toolsFile := getAlloyDBCreateToolsConfig()
|
||||
toolsFile := getAlloyDBToolsConfig()
|
||||
cmd, cleanupCmd, err := tests.StartCmd(ctx, toolsFile, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("command initialization returned an error: %v", err)
|
||||
@@ -1209,72 +1238,189 @@ func TestAlloyDBCreateUser(t *testing.T) {
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "successful creation IAM user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u1-iam-success", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `{
|
||||
"databaseRoles": ["alloydbiamuser", "alloydbsuperuser"],
|
||||
"name": "projects/p1/locations/l1/clusters/c1/users/u1-iam-success",
|
||||
"userType": "ALLOYDB_IAM_USER"
|
||||
}`,
|
||||
name: "successful creation",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "instance": "i1-success", "instanceType": "PRIMARY", "displayName": "i1-success"}`,
|
||||
want: `{"metadata":{"@type":"type.googleapis.com/google.cloud.alloydb.v1.OperationMetadata","target":"projects/p1/locations/l1/clusters/c1/instances/i1-success","verb":"create","requestedCancellation":false,"apiVersion":"v1"},"name":"projects/p1/locations/l1/operations/mock-operation-success"}`,
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "successful creation builtin user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u2-builtin-success", "userType": "ALLOYDB_BUILT_IN", "password": "pass123"}`,
|
||||
want: `{
|
||||
"databaseRoles": ["alloydbsuperuser"],
|
||||
"name": "projects/p1/locations/l1/clusters/c1/users/u2-builtin-success",
|
||||
"userType": "ALLOYDB_BUILT_IN"
|
||||
}`,
|
||||
wantStatusCode: http.StatusOK,
|
||||
name: "api failure",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "instance": "i2-api-failure", "instanceType": "PRIMARY", "displayName": "i1-success"}`,
|
||||
want: "internal api error",
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing project",
|
||||
body: `{"location": "l1", "cluster": "c1", "instance": "i1", "instanceType": "PRIMARY"}`,
|
||||
want: `parameter \"project\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing cluster",
|
||||
body: `{"project": "p1", "location": "l1", "instance": "i1", "instanceType": "PRIMARY"}`,
|
||||
want: `parameter \"cluster\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing location",
|
||||
body: `{"project": "p1", "cluster": "c1", "instance": "i1", "instanceType": "PRIMARY"}`,
|
||||
want: `parameter \"location\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing instance",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "instanceType": "PRIMARY"}`,
|
||||
want: `parameter \"instance\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "invalid instanceType",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "instance": "i1", "instanceType": "INVALID", "displayName": "invalid"}`,
|
||||
want: `invalid 'instanceType' parameter; expected 'PRIMARY' or 'READ_POOL'`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tcs {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
api := "http://127.0.0.1:5000/api/tool/alloydb-create-instance/invoke"
|
||||
req, err := http.NewRequest(http.MethodPost, api, bytes.NewBufferString(tc.body))
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create request: %s", err)
|
||||
}
|
||||
req.Header.Add("Content-type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to send request: %s", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
bodyBytes, _ := io.ReadAll(resp.Body)
|
||||
|
||||
if resp.StatusCode != tc.wantStatusCode {
|
||||
t.Fatalf("expected status %d but got %d: %s", tc.wantStatusCode, resp.StatusCode, string(bodyBytes))
|
||||
}
|
||||
|
||||
if tc.wantStatusCode != http.StatusOK {
|
||||
if tc.want != "" && !bytes.Contains(bodyBytes, []byte(tc.want)) {
|
||||
t.Fatalf("expected error response to contain %q, but got: %s", tc.want, string(bodyBytes))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("response status code is not 200, got %d: %s", resp.StatusCode, string(bodyBytes))
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Result string `json:"result"`
|
||||
}
|
||||
if err := json.Unmarshal(bodyBytes, &result); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
|
||||
var got, want map[string]any
|
||||
if err := json.Unmarshal([]byte(result.Result), &got); err != nil {
|
||||
t.Fatalf("failed to unmarshal result: %v", err)
|
||||
}
|
||||
if err := json.Unmarshal([]byte(tc.want), &want); err != nil {
|
||||
t.Fatalf("failed to unmarshal want: %v", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(want, got) {
|
||||
t.Errorf("unexpected result:\n- want: %+v\n- got: %+v", want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAlloyDBCreateUser(t *testing.T) {
|
||||
cleanup := setupTestServer(t, "userId")
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer cancel()
|
||||
|
||||
var args []string
|
||||
toolsFile := getAlloyDBToolsConfig()
|
||||
cmd, cleanupCmd, err := tests.StartCmd(ctx, toolsFile, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("command initialization returned an error: %v", err)
|
||||
}
|
||||
defer cleanupCmd()
|
||||
|
||||
waitCtx, cancelWait := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancelWait()
|
||||
out, err := testutils.WaitForString(waitCtx, regexp.MustCompile(`Server ready to serve`), cmd.Out)
|
||||
if err != nil {
|
||||
t.Logf("toolbox command logs: \n%s", out)
|
||||
t.Fatalf("toolbox didn't start successfully: %s", err)
|
||||
}
|
||||
|
||||
tcs := []struct {
|
||||
name string
|
||||
body string
|
||||
want string
|
||||
wantStatusCode int
|
||||
}{
|
||||
{
|
||||
name: "successful creation IAM user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u1-iam-success", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `{"databaseRoles": ["alloydbiamuser"], "name": "projects/p1/locations/l1/clusters/c1/users/u1-iam-success", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "api failure",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u3-api-failure", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: "user internal api error",
|
||||
name: "successful creation builtin user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u2-builtin-success", "userType": "ALLOYDB_BUILT_IN", "password": "pass123", "databaseRoles": ["alloydbsuperuser"]}`,
|
||||
want: `{"databaseRoles": ["alloydbsuperuser"], "name": "projects/p1/locations/l1/clusters/c1/users/u2-builtin-success", "userType": "ALLOYDB_BUILT_IN"}`,
|
||||
wantStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
name: "api failure",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u3-api-failure", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: "user internal api error",
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing project",
|
||||
body: `{"location": "l1", "cluster": "c1", "user": "u-fail", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"project\" is required`,
|
||||
name: "missing project",
|
||||
body: `{"location": "l1", "cluster": "c1", "user": "u-fail", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"project\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing cluster",
|
||||
body: `{"project": "p1", "location": "l1", "user": "u-fail", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"cluster\" is required`,
|
||||
name: "missing cluster",
|
||||
body: `{"project": "p1", "location": "l1", "user": "u-fail", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"cluster\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing location",
|
||||
body: `{"project": "p1", "cluster": "c1", "user": "u-fail", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"location\" is required`,
|
||||
name: "missing location",
|
||||
body: `{"project": "p1", "cluster": "c1", "user": "u-fail", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"location\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"user\" is required`,
|
||||
name: "missing user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "userType": "ALLOYDB_IAM_USER"}`,
|
||||
want: `parameter \"user\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing userType",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u-fail"}`,
|
||||
want: `parameter \"userType\" is required`,
|
||||
name: "missing userType",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u-fail"}`,
|
||||
want: `parameter \"userType\" is required`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "missing password for builtin user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u-fail", "userType": "ALLOYDB_BUILT_IN"}`,
|
||||
want: `password is required when userType is ALLOYDB_BUILT_IN`,
|
||||
name: "missing password for builtin user",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u-fail", "userType": "ALLOYDB_BUILT_IN"}`,
|
||||
want: `password is required when userType is ALLOYDB_BUILT_IN`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "invalid userType",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u-fail", "userType": "invalid"}`,
|
||||
want: `invalid or missing 'userType' parameter; expected 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'`,
|
||||
name: "invalid userType",
|
||||
body: `{"project": "p1", "location": "l1", "cluster": "c1", "user": "u-fail", "userType": "invalid"}`,
|
||||
want: `invalid or missing 'userType' parameter; expected 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'`,
|
||||
wantStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
@@ -1327,25 +1473,3 @@ func TestAlloyDBCreateUser(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getAlloyDBCreateToolsConfig() map[string]any {
|
||||
return map[string]any{
|
||||
"sources": map[string]any{
|
||||
"my-alloydb-source": map[string]any{
|
||||
"kind": "alloydb-admin",
|
||||
},
|
||||
},
|
||||
"tools": map[string]any{
|
||||
"alloydb-create-cluster": map[string]any{
|
||||
"kind": "alloydb-create-cluster",
|
||||
"description": "create cluster",
|
||||
"source": "my-alloydb-source",
|
||||
},
|
||||
"alloydb-create-user": map[string]any{
|
||||
"kind": "alloydb-create-user",
|
||||
"description": "create user",
|
||||
"source": "my-alloydb-source",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user