package com.sequenceiq.periscope.client; import java.util.Collections; import java.util.concurrent.TimeUnit; import javax.ws.rs.client.Client; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.Form; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import org.glassfish.jersey.client.proxy.WebResourceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sequenceiq.cloudbreak.client.AccessToken; import com.sequenceiq.cloudbreak.client.ConfigKey; import com.sequenceiq.cloudbreak.client.IdentityClient; import com.sequenceiq.cloudbreak.client.RestClientUtil; import com.sequenceiq.periscope.api.AutoscaleApi; import com.sequenceiq.periscope.api.endpoint.AlertEndpoint; import com.sequenceiq.periscope.api.endpoint.ClusterEndpoint; import com.sequenceiq.periscope.api.endpoint.ConfigurationEndpoint; import com.sequenceiq.periscope.api.endpoint.HistoryEndpoint; import com.sequenceiq.periscope.api.endpoint.PolicyEndpoint; import net.jodah.expiringmap.ExpirationPolicy; import net.jodah.expiringmap.ExpiringMap; public class AutoscaleClient { private static final Logger LOGGER = LoggerFactory.getLogger(AutoscaleClient.class); private static final Form EMPTY_FORM = new Form(); private static final String TOKEN_KEY = "TOKEN"; private static final double TOKEN_EXPIRATION_FACTOR = 0.9; private final ExpiringMap<String, String> tokenCache; private final Client client; private final IdentityClient identityClient; private final String autoscaleAddress; private String user; private String password; private String secret; private WebTarget t; private AlertEndpoint alertEndpoint; private ClusterEndpoint clusterEndpoint; private ConfigurationEndpoint configurationEndpoint; private HistoryEndpoint historyEndpoint; private PolicyEndpoint policyEndpoint; private AutoscaleClient(String autoscaleAddress, String identityServerAddress, String user, String password, String clientId, ConfigKey configKey) { this.client = RestClientUtil.get(configKey); this.autoscaleAddress = autoscaleAddress; this.identityClient = new IdentityClient(identityServerAddress, clientId, configKey); this.user = user; this.password = password; this.tokenCache = configTokenCache(); refresh(); LOGGER.info("AutoscaleClient has been created with user / pass. autoscale: {}, identity: {}, clientId: {}, configKey: {}", autoscaleAddress, identityServerAddress, clientId, configKey); } private AutoscaleClient(String autoscaleAddress, String identityServerAddress, String secret, String clientId, ConfigKey configKey) { this.client = RestClientUtil.get(configKey); this.autoscaleAddress = autoscaleAddress; this.identityClient = new IdentityClient(identityServerAddress, clientId, configKey); this.secret = secret; this.tokenCache = configTokenCache(); refresh(); LOGGER.info("AutoscaleClient has been created with a secret. autoscale: {}, identity: {}, clientId: {}, configKey: {}", autoscaleAddress, identityServerAddress, clientId, configKey); } private ExpiringMap<String, String> configTokenCache() { return ExpiringMap.builder().variableExpiration().expirationPolicy(ExpirationPolicy.CREATED).build(); } private synchronized void refresh() { String token = tokenCache.get(TOKEN_KEY); if (token == null) { AccessToken accessToken; if (secret != null) { accessToken = identityClient.getToken(secret); } else { accessToken = identityClient.getToken(user, password); } token = accessToken.getToken(); int exp = (int) (accessToken.getExpiresIn() * TOKEN_EXPIRATION_FACTOR); LOGGER.info("Token has been renewed and expires in {} seconds", exp); tokenCache.put(TOKEN_KEY, accessToken.getToken(), ExpirationPolicy.CREATED, exp, TimeUnit.SECONDS); renewEndpoints(token); } } private void renewEndpoints(String token) { MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>(); headers.add("Authorization", "Bearer " + token); this.t = client.target(autoscaleAddress).path(AutoscaleApi.API_ROOT_CONTEXT); this.alertEndpoint = newResource(AlertEndpoint.class, headers); this.clusterEndpoint = newResource(ClusterEndpoint.class, headers); this.configurationEndpoint = newResource(ConfigurationEndpoint.class, headers); this.historyEndpoint = newResource(HistoryEndpoint.class, headers); this.policyEndpoint = newResource(PolicyEndpoint.class, headers); LOGGER.info("Endpoints have been renewed for AutoscaleClient"); } private <C> C newResource(final Class<C> resourceInterface, MultivaluedMap<String, Object> headers) { return WebResourceFactory.newResource(resourceInterface, t, false, headers, Collections.emptyList(), EMPTY_FORM); } public AlertEndpoint alertEndpoint() { refresh(); return alertEndpoint; } public ClusterEndpoint clusterEndpoint() { refresh(); return clusterEndpoint; } public ConfigurationEndpoint configurationEndpoint() { refresh(); return configurationEndpoint; } public HistoryEndpoint historyEndpoint() { refresh(); return historyEndpoint; } public PolicyEndpoint policyEndpoint() { refresh(); return policyEndpoint; } public static class AutoscaleClientBuilder { private final String autoscaleAddress; private final String identityServerAddress; private final String clientId; private String user; private String password; private String secret; private boolean debug; private boolean secure = true; public AutoscaleClientBuilder(String autoscaleAddress, String identityServerAddress, String clientId) { this.autoscaleAddress = autoscaleAddress; this.identityServerAddress = identityServerAddress; this.clientId = clientId; } public AutoscaleClientBuilder withCredential(String user, String password) { this.user = user; this.password = password; return this; } public AutoscaleClientBuilder withSecret(String secret) { this.secret = secret; return this; } public AutoscaleClientBuilder withDebug(boolean debug) { this.debug = debug; return this; } public AutoscaleClientBuilder withCertificateValidation(boolean secure) { this.secure = secure; return this; } public AutoscaleClient build() { ConfigKey configKey = new ConfigKey(secure, debug); if (secret != null) { return new AutoscaleClient(autoscaleAddress, identityServerAddress, secret, clientId, configKey); } else { return new AutoscaleClient(autoscaleAddress, identityServerAddress, user, password, clientId, configKey); } } } }