package io.cattle.platform.service.launcher; import static io.cattle.platform.core.model.tables.AccountTable.*; import static io.cattle.platform.core.model.tables.CredentialTable.*; import io.cattle.platform.core.constants.AccountConstants; import io.cattle.platform.core.constants.CommonStatesConstants; import io.cattle.platform.core.constants.CredentialConstants; import io.cattle.platform.core.dao.AccountDao; import io.cattle.platform.core.dao.GenericResourceDao; import io.cattle.platform.core.model.Account; import io.cattle.platform.core.model.Credential; import io.cattle.platform.deferred.util.DeferredUtils; import io.cattle.platform.engine.process.util.ProcessEngineUtils; import io.cattle.platform.lock.LockCallback; import io.cattle.platform.lock.LockDelegator; import io.cattle.platform.lock.LockManager; import io.cattle.platform.lock.definition.LockDefinition; import io.cattle.platform.object.process.ObjectProcessManager; import io.cattle.platform.object.process.StandardProcess; import io.cattle.platform.object.resource.ResourceMonitor; import io.cattle.platform.process.common.util.ProcessUtils; import io.cattle.platform.util.type.InitializationTask; import java.io.IOException; import java.lang.ProcessBuilder.Redirect; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.inject.Inject; import org.apache.cloudstack.managed.context.NoExceptionRunnable; import com.netflix.config.DynamicStringProperty; public abstract class GenericServiceLauncher extends NoExceptionRunnable implements InitializationTask, Runnable { private static final String SERVICE_USER_UUID = "machineServiceAccount"; private static final String SERVICE_USER_NAME = "System Service"; private static final int WAIT = 2000; @Inject LockManager lockManager; @Inject LockDelegator lockDelegator; @Inject ScheduledExecutorService executor; @Inject AccountDao accountDao; @Inject GenericResourceDao resourceDao; @Inject ResourceMonitor resourceMonitor; @Inject ObjectProcessManager processManager; Process process; ScheduledFuture<?> future; @Override public void start() { Runnable cb = (new Runnable() { @Override public void run() { reload(); } }); future = executor.scheduleWithFixedDelay(this, WAIT, WAIT, TimeUnit.MILLISECONDS); List<DynamicStringProperty> reloadList = getReloadSettings(); if (reloadList != null) { for(DynamicStringProperty reload : reloadList) { if (reload != null) { reload.addCallback(cb); } } } } public void reload() { processDestroy(); } public void stop() { if (future != null) { future.cancel(true); } processDestroy(); } protected synchronized void processDestroy() { if (process != null) { process.destroy(); process = null; } } protected String getAccountUuid() { return SERVICE_USER_UUID; } protected String getAccountName() { return SERVICE_USER_NAME; } protected List<DynamicStringProperty> getReloadSettings() { return null; } public Credential getCredential() { return lockManager.lock(new ServiceCredLock(SERVICE_USER_UUID), new LockCallback<Credential>() { @Override public Credential doWithLock() { return getCredentialLock(); } }); } protected Credential getCredentialLock() { Account account = accountDao.findByUuid(SERVICE_USER_UUID); if (account == null) { account = DeferredUtils.nest(new Callable<Account>() { @Override public Account call() throws Exception { return resourceDao.createAndSchedule(Account.class, ACCOUNT.UUID, getAccountUuid(), ACCOUNT.NAME, getAccountName(), ACCOUNT.KIND, AccountConstants.SERVICE_KIND); } }); } final Long accountId = account.getId(); account = resourceMonitor.waitForState(account, CommonStatesConstants.ACTIVE); List<? extends Credential> creds = accountDao.getApiKeys(account, CredentialConstants.KIND_AGENT_API_KEY, false); Credential cred = creds.size() > 0 ? creds.get(0) : null; /* This is to fix a bug in which we ended up with a lot of api keys created */ for (int i = 1; i < creds.size(); i++) { processManager.scheduleStandardProcessAsync(StandardProcess.DEACTIVATE, creds.get(i), ProcessUtils.chainInData(new HashMap<String, Object>(), CredentialConstants.PROCESSS_DEACTIVATE, CredentialConstants.PROCESSS_REMOVE)); } if (cred == null) { cred = DeferredUtils.nest(new Callable<Credential>() { @Override public Credential call() throws Exception { return resourceDao.createAndSchedule(Credential.class, CREDENTIAL.KIND, CredentialConstants.KIND_AGENT_API_KEY, CREDENTIAL.ACCOUNT_ID, accountId); } }); } return resourceMonitor.waitForState(cred, CommonStatesConstants.ACTIVE); } protected abstract boolean shouldRun(); protected abstract boolean isReady(); protected abstract String binaryPath(); protected abstract void setEnvironment(Map<String, String> env); protected abstract LockDefinition getLock(); @Override protected synchronized void doRun() throws Exception { if (!ProcessEngineUtils.enabled() || !shouldRun() || !isReady()) { return; } LockDefinition lock = getLock(); if (lock != null && !lockDelegator.tryLock(lock)) { return; } boolean launch = false; if (process == null) { launch = true; } else { try { process.exitValue(); launch = true; process.waitFor(); } catch (IllegalThreadStateException e) { // ignore } catch (InterruptedException e) { // ignore } } if (!launch) { return; } ProcessBuilder pb = new ProcessBuilder(binaryPath()); Map<String, String> env = pb.environment(); setEnvironment(env); prepareProcess(pb); pb.redirectOutput(Redirect.INHERIT); pb.redirectError(Redirect.INHERIT); try { process = pb.start(); } catch (IOException e) { throw new RuntimeException(e); } } protected void prepareProcess(ProcessBuilder pb) throws IOException { } }