Final update (sorry to spam this thread but I know somebody will appreciate this being documented in the future):
I was able to get this working by just using the S3 client settings belonging to Deployment-B to configure the read-only snapshot bucket form Deployment A.
# updated code:
def register_repo(
target_cluster: ElasticCluster,
target_repo: str,
remote_repo: Dict, # result of get_repo_info()
s3_client: str, # S3 client inside **target** env to use for reading remote repo
) -> bool:
"""
Registers the provided snapshot repo, `remote_repo`, inside of `target_cluster` for read (only) access under the name `target_repo`.
Returns True on success.
Visit /app/management/data/snapshot_restore/repositories in Kibana to see repos.
https://www.elastic.co/guide/en/elasticsearch/reference/current/put-snapshot-repo-api.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/snapshots-register-repository.html
"""
remote_repo = copy.deepcopy(remote_repo)
# https://www.elastic.co/guide/en/elasticsearch/reference/current/repository-s3.html
remote_repo["settings"]["readonly"] = True
remote_repo["settings"]["client"] = s3_client # e.g. can't use PRD client in TST
# repo_info["settings"]["compress"] = False
res = es_request(
cluster=target_cluster,
method="PUT",
path=f"/_snapshot/{target_repo}",
json=remote_repo,
)
def get_repo_info(cluster: ElasticCluster, repo_name: str) -> Optional[Dict]:
"""Get metadata about a snapshot repo e.g. {'type': 's3', 'uuid': ...}"""
res = es_request(cluster, "GET", f"/_snapshot/{repo_name}")
if res.status_code != 200:
logger.warning(
f"failed to get repo info for '{repo_name}': status {res.status_code}, {res.text}"
)
return None
return res.json()[repo_name]
def es_request(
cluster: ElasticCluster, method: str, path: str, **httpx_kwargs
) -> httpx.Response:
"""
Wrapper for making an arbitrary request to Elasticsearch.
Based on core/app/utils/elasticsearch_http.py but synchronous for simplicity.
"""
headers = {
"Authorization": f"ApiKey {cluster.elasticsearch_api_key}",
"Accept-Encoding": "gzip", # prefer compressed responses
}
es_url = cluster.elasticsearch_host.rstrip("/") + "/" + path.lstrip("/")
res = httpx.request(method, es_url, headers=headers, **httpx_kwargs)
return res
This works for my existing environments, but interestingly didn't work for a new environment I made from scratch temporarily to test (which got an AWS access error). This is good enough for me now because my existing environments are all I care about.
The alternative solution to all of this: would theoretically be to create my own S3 bucket in my own aws account, configure it in Deployment-A as a snapshot bucket (via the API) with a snapshot lifecycle plan, configure in my other environments as a read only bucket, and provide credentials to all my deployments so they can read from this bucket (and so Deployment-A can write to it). At least here you'd have full control of the access management of each environment and that wouldn't be so opaque.
And for step 4 I first make an API call that deletes all the indices I'm going to restore, prior to the restore API call.