All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.volcengine.tos.credential.EcsCredentialsProvider Maven / Gradle / Ivy

package com.volcengine.tos.credential;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.volcengine.tos.TosClientException;
import com.volcengine.tos.comm.HttpStatus;
import com.volcengine.tos.internal.util.StringUtils;
import com.volcengine.tos.internal.util.TosUtils;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.io.Closeable;
import java.io.IOException;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class EcsCredentialsProvider implements CredentialsProvider, Closeable {
    private static final String DEFAULT_META_SERVICE_URL = "http://100.96.0.96/volcstack/latest/iam/security_credentials";
    private static final DateTimeFormatter expireTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");

    private final Thread refreshThread;
    private final String roleName;
    private final String url;
    private final Lock lock;
    private final OkHttpClient client;
    private volatile EcsCredentials ecsCredentials;

    public EcsCredentialsProvider(String roleName) {
        this(roleName, null);
    }

    public EcsCredentialsProvider(String roleName, String url) {
        if (StringUtils.isEmpty(roleName)) {
            throw new TosClientException("ecs role name is empty", null);
        }
        if (StringUtils.isEmpty(url)) {
            this.url = DEFAULT_META_SERVICE_URL;
        } else {
            this.url = url;
        }
        this.roleName = roleName;
        this.lock = new ReentrantLock();
        this.client = TosUtils.defaultOkHttpClient();
        // 5min interval
        final int finalRefreshInterval = 300;
        this.refreshThread = new Thread() {
            public void run() {

                while (!Thread.interrupted()) {
                    try {
                        Thread.sleep(finalRefreshInterval * 1000);
                        EcsCredentials origin = EcsCredentialsProvider.this.ecsCredentials;
                        if (origin == null || System.nanoTime() - origin.lastUpdateTimeNanos > 1 * 1e9) {
                            EcsCredentialsProvider.this.fetchCredentials();
                        }
                    } catch (TosClientException e) {
                        TosUtils.getLogger().warn("try to fetch ecs credentials failed", e);
                    } catch (InterruptedException e) {
                        return;
                    }
                }
            }
        };
        this.refreshThread.setDaemon(true);
        this.refreshThread.start();
    }

    @Override
    public Credentials getCredentials(int expires) {
        EcsCredentials credentials = this.ecsCredentials;
        if (credentials != null && credentials.isValid()) {
            return credentials;
        }
        this.lock.lock();
        try {
            credentials = this.ecsCredentials;
            if (credentials != null && credentials.isValid()) {
                return credentials;
            }
            return this.fetchCredentials();
        } finally {
            this.lock.unlock();
        }
    }

    private Credentials fetchCredentials() {
        EcsCredentials origin = this.ecsCredentials;
        Request.Builder builder = new Request.Builder().url(url + "/" + roleName).method("GET", null);
        try (Response response = this.client.newCall(builder.build()).execute()) {
            if (response.code() == HttpStatus.OK) {
                if (response.body() != null) {
                    EcsCredentials current = TosUtils.getJsonMapper().readValue(response.body().byteStream(), EcsCredentials.class);
                    if (StringUtils.isNotEmpty(current.ak) && StringUtils.isNotEmpty(current.sk)) {
                        current.lastUpdateTimeNanos = System.nanoTime();
                        if (StringUtils.isNotEmpty(current.expiredTime)) {
                            ZonedDateTime l = ZonedDateTime.parse(current.expiredTime, expireTimeFormatter);
                            current.expiredDate = Date.from(Instant.from(l));
                        }
                        this.ecsCredentials = current;
                        return current;
                    }
                }
                throw new TosClientException("parse ecs token failed", null);
            }
            throw new TosClientException("get ecs token failed, unexpected status code: " + response.code(), null);
        } catch (Exception e) {
            if (origin != null) {
                origin.immortal = true;
                return origin;
            }

            if (e instanceof TosClientException) {
                throw (TosClientException) e;
            }
            throw new TosClientException("get ecs token failed", e);
        }
    }

    @Override
    public void close() throws IOException {
        this.refreshThread.interrupt();
        this.client.connectionPool().evictAll();
    }

    private static class EcsCredentials implements Credentials {
        @JsonIgnore
        volatile boolean immortal;
        @JsonIgnore
        long lastUpdateTimeNanos;
        @JsonIgnore
        Date expiredDate;
        @JsonProperty("AccessKeyId")
        String ak;
        @JsonProperty("SecretAccessKey")
        String sk;
        @JsonProperty("SessionToken")
        String securityToken;
        @JsonProperty("ExpiredTime")
        String expiredTime;

        boolean isValid() {
            return immortal || expiredDate == null || expiredDate.after(new Date());
        }

        @Override
        public String getAk() {
            return ak;
        }

        @Override
        public String getSk() {
            return sk;
        }

        @Override
        public String getSecurityToken() {
            return securityToken;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy