OpenStack4j是一个OpenStack的Java SDK。
问题描述
在同一个代码处理线程中,首先获取了 projectA 的 OSClient 对象 OSClientA,然后又获取了 projectB 的 OSClient 对象 OSClientB。
后续在用 OSClientA 去调用某个 service(比如 BlockVolumeService)去创建资源(比如 volume)的时候,期望创建在 projectA 下面,结果创建的资源却在 projectB 下面。
查找原因
经过跟踪 OpenStack4j 中获取 OSClient 和 调用具体 service 的相关源码后,发现问题,在于 OSClientSession 类中使用 ThreadLocal 变量 sessions 将获取的 OSClient 存下来。
后续在创建资源的时候,使用从 sessions 中取出的OSClient ,调用 OpenStack 的 API 接口。
关键在于,第二次获取 OSClientB 的时候,会将 sessions 中存的 OSClient 更新,将原先的 OSClientA 给替换为 OSClientB。
也就造成了,尽管是用 OSClientA 去创建资源,但是实际使用的 OSClient 已经被改了,也就是是用 OSClientB 的相关参数去创建的。
源码分析
获取 OSClient 的代码在 OSAuthenticator#authenticateV3(默认使用的是v3版本)。
......
private static OSClientV3 authenticateV3(KeystoneAuth auth, SessionInfo info, Config config) {
if (auth.getType().equals(Type.TOKENLESS)){
......
}
# 调用 OpenStack keystone 的认证接口
HttpRequest request = HttpRequest.builder(KeystoneToken.class)
.header(ClientConstants.HEADER_OS4J_AUTH, TOKEN_INDICATOR).endpoint(info.endpoint)
.method(HttpMethod.POST).path("/auth/tokens").config(config).entity(auth).build();
HttpResponse response = HttpExecutor.create().execute(request);
if (response.getStatus() >= 400) {<