OpenSearch API
OpenSearch API client that allows to prepare the data or setup access roles for existing Opensearch clusters. The construct supports both OpenSearch provisioned clusters and OpenSearch Serverless collections.
Overview
The construct leverages the CDK Provider Framework to deploy a custom resource to manage, and provide addRoleMapping
and callOpenSearchApi
methods. Both methods return the custom resource so that allows to enforce sequental execution of the API calls. By default all API calls will be executed simultaneously and are independent of each other.
- TypeScript
- Python
const domainEndpoint='search-XXXXXX.XXXXXX.es.amazonaws.com';
const apiRole = Role.fromRoleName(this, 'ApiRole', '<IAMRoleWithOpenSearchPermissions>');
const osApi = new dsf.consumption.OpenSearchApi(this, 'MyOpenSearchApi',{
iamHandlerRole:apiRole,
openSearchEndpoint:domainEndpoint,
openSearchClusterType:dsf.consumption.OpenSearchClusterType.PROVISIONED,
removalPolicy:cdk.RemovalPolicy.DESTROY
});
domain_endpoint = "search-XXXXXX.XXXXXX.es.amazonaws.com"
api_role = Role.from_role_name(self, "ApiRole", "<IAMRoleWithOpenSearchPermissions>")
os_api = dsf.consumption.OpenSearchApi(self, "MyOpenSearchApi",
iam_handler_role=api_role,
open_search_endpoint=domain_endpoint,
open_search_cluster_type=dsf.consumption.OpenSearchClusterType.PROVISIONED,
removal_policy=cdk.RemovalPolicy.DESTROY
)
The IAM Role passed as iamHandlerRole
property has to have all necessary permissions to execute API calls to the cluster.
callOpenSearchApi
Generic method to execute any Opensearch API, subject to correct permissions attached to the IAM Role.
- TypeScript
- Python
//create index template
const indexTemplateCr = osApi.callOpenSearchApi('CreateIndexTemplate','_index_template/movies',
{
"index_patterns": [
"movies-*"
],
"template": {
"settings": {
"index": {
"number_of_shards": 1,
"number_of_replicas": 0
}
},
"mappings": {
"properties": {
"title": {
"type": "text"
},
"year": {
"type": "integer"
}
}
}
}
});
const metadata='{ "index" : { "_index" : "movies-02"}}';
const bulk=`${metadata}
{"title": "Barbie", "year": 2023}
${metadata}
{"title": "Openheimer", "year": 2023}`;
// bulk ingestion using POST
const bulkCr = osApi.callOpenSearchApi('AddBulk','_bulk',bulk+'\n\n','POST');
//dependency to enforce sequential API calls
bulkCr.node.addDependency(indexTemplateCr);
const add1Cr = osApi.callOpenSearchApi('AddData1', 'movies-01/_doc/1111',{"title": "Rush", "year": 2013}, 'PUT');
add1Cr.node.addDependency(indexTemplateCr);
const add2Cr = osApi.callOpenSearchApi('AddData3', 'movies-01/_doc/2222',{"title": "Toy Story", "year": 2014}, 'PUT');
add2Cr.node.addDependency(indexTemplateCr);
const add3Cr = osApi.callOpenSearchApi('AddData4', 'movies-01/_doc',{"title": "The Little Mermaid", "year": 2015}, 'POST');
add3Cr.node.addDependency(indexTemplateCr);
# create index template
index_template_cr = os_api.call_open_search_api("CreateIndexTemplate", "_index_template/movies", {
"index_patterns": ["movies-*"
],
"template": {
"settings": {
"index": {
"number_of_shards": 1,
"number_of_replicas": 0
}
},
"mappings": {
"properties": {
"title": {
"type": "text"
},
"year": {
"type": "integer"
}
}
}
}
})
metadata = "{ \"index\" : { \"_index\" : \"movies-02\"}}"
bulk = f"""{metadata}
{\"title\": \"Barbie\", \"year\": 2023}
{metadata}
{\"title\": \"Openheimer\", \"year\": 2023}"""
# bulk ingestion using POST
bulk_cr = os_api.call_open_search_api("AddBulk", "_bulk", bulk + "\n\n", "POST")
# dependency to enforce sequential API calls
bulk_cr.node.add_dependency(index_template_cr)
add1_cr = os_api.call_open_search_api("AddData1", "movies-01/_doc/1111", {"title": "Rush", "year": 2013}, "PUT")
add1_cr.node.add_dependency(index_template_cr)
add2_cr = os_api.call_open_search_api("AddData3", "movies-01/_doc/2222", {"title": "Toy Story", "year": 2014}, "PUT")
add2_cr.node.add_dependency(index_template_cr)
add3_cr = os_api.call_open_search_api("AddData4", "movies-01/_doc", {"title": "The Little Mermaid", "year": 2015}, "POST")
add3_cr.node.add_dependency(index_template_cr)
addRoleMapping
Use this method to add role mappings to OpenSearch cluster using _security
plugin.
This method is only applicable to provisioned OpenSearch clusters.
OpenSearch Roles API does not allow to update individual roles, requiring to pass array of roles that needs to be applied.
To avoid overwriting prevously added roles addRoleMapping
method provides persist
parameter to store previously added roles inside the construct. To avoid racing conditions you also need to execute multiple addRoleMapping
calls sequentionally as shown below.
- TypeScript
- Python
const domainEndpoint='search-XXXXXX.XXXXXX.es.amazonaws.com';
const apiRole = Role.fromRoleName(this, 'ApiRole', '<IAMRoleWithOpenSearchPermissions>');
const osApi = new dsf.consumption.OpenSearchApi(this, 'MyOpenSearchApi',{
iamHandlerRole:apiRole,
openSearchEndpoint:domainEndpoint,
openSearchClusterType:dsf.consumption.OpenSearchClusterType.PROVISIONED,
removalPolicy:cdk.RemovalPolicy.DESTROY
});
const firstCall = osApi.addRoleMapping('AnotherAdmin', 'all_access','<IAMRole>', true);
const secondCall = osApi.addRoleMapping('AnotherAdmin', 'all_access','<IAMRole>', true);
//dependency to enforce sequential API calls
secondCall.node.addDependency(firstCall);
domain_endpoint = "search-XXXXXX.XXXXXX.es.amazonaws.com"
api_role = Role.from_role_name(self, "ApiRole", "<IAMRoleWithOpenSearchPermissions>")
os_api = dsf.consumption.OpenSearchApi(self, "MyOpenSearchApi",
iam_handler_role=api_role,
open_search_endpoint=domain_endpoint,
open_search_cluster_type=dsf.consumption.OpenSearchClusterType.PROVISIONED,
removal_policy=cdk.RemovalPolicy.DESTROY
)
first_call = os_api.add_role_mapping("AnotherAdmin", "all_access", "<IAMRole>", True)
second_call = os_api.add_role_mapping("AnotherAdmin", "all_access", "<IAMRole>", True)
# dependency to enforce sequential API calls
second_call.node.add_dependency(first_call)