package io.cattle.platform.configitem.version.dao.impl;
import static io.cattle.platform.core.model.tables.AccountTable.*;
import static io.cattle.platform.core.model.tables.AgentTable.*;
import static io.cattle.platform.core.model.tables.ConfigItemStatusTable.*;
import static io.cattle.platform.core.model.tables.ConfigItemTable.*;
import static io.cattle.platform.core.model.tables.HostTable.*;
import static io.cattle.platform.core.model.tables.InstanceTable.*;
import static io.cattle.platform.core.model.tables.ServiceTable.*;
import static io.cattle.platform.core.model.tables.StackTable.*;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.configitem.events.ConfigUpdated;
import io.cattle.platform.configitem.model.Client;
import io.cattle.platform.configitem.model.DefaultItemVersion;
import io.cattle.platform.configitem.model.ItemVersion;
import io.cattle.platform.configitem.request.ConfigUpdateItem;
import io.cattle.platform.configitem.request.ConfigUpdateRequest;
import io.cattle.platform.configitem.version.dao.ConfigItemStatusDao;
import io.cattle.platform.core.constants.AgentConstants;
import io.cattle.platform.core.constants.CommonStatesConstants;
import io.cattle.platform.core.constants.InstanceConstants;
import io.cattle.platform.core.model.Account;
import io.cattle.platform.core.model.Agent;
import io.cattle.platform.core.model.ConfigItem;
import io.cattle.platform.core.model.ConfigItemStatus;
import io.cattle.platform.core.model.Host;
import io.cattle.platform.core.model.Service;
import io.cattle.platform.core.model.Stack;
import io.cattle.platform.core.model.tables.records.ConfigItemStatusRecord;
import io.cattle.platform.db.jooq.dao.impl.AbstractJooqDao;
import io.cattle.platform.deferred.util.DeferredUtils;
import io.cattle.platform.eventing.EventService;
import io.cattle.platform.metrics.util.MetricsUtil;
import io.cattle.platform.object.ObjectManager;
import io.cattle.platform.util.type.CollectionUtils;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import org.jooq.Condition;
import org.jooq.Record2;
import org.jooq.TableField;
import org.jooq.exception.DataAccessException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.Timer;
import com.codahale.metrics.Timer.Context;
import com.netflix.config.DynamicIntProperty;
public class ConfigItemStatusDaoImpl extends AbstractJooqDao implements ConfigItemStatusDao {
private static final Logger log = LoggerFactory.getLogger(ConfigItemStatusDaoImpl.class);
private static final DynamicIntProperty BATCH_SIZE = ArchaiusUtil.getInt("item.sync.batch.size");
@Inject
EventService eventService;
ObjectManager objectManager;
Timer incrementTimer = MetricsUtil.getRegistry().timer("config.item.increment");
Timer appliedTimer = MetricsUtil.getRegistry().timer("config.item.applied");
@Override
public void incrementOrApply(Client client, String itemName) {
Context t = incrementTimer.time();
try {
if ( ! increment(client, itemName) ) {
RuntimeException e = apply(client, itemName);
if ( e != null ) {
if ( ! increment(client, itemName) ) {
throw new IllegalStateException("Failed to increment [" + itemName + "] on [" + client + "]", e);
}
}
}
} finally {
t.stop();
}
}
@Override
public Long getRequestedVersion(Client client, String itemName) {
return create()
.select(CONFIG_ITEM_STATUS.REQUESTED_VERSION)
.from(CONFIG_ITEM_STATUS)
.where(
CONFIG_ITEM_STATUS.NAME.eq(itemName)
.and(targetObjectCondition(client)))
.fetchOneInto(Long.class);
}
protected boolean increment(Client client, String itemName) {
int updated = create()
.update(CONFIG_ITEM_STATUS)
.set(CONFIG_ITEM_STATUS.REQUESTED_VERSION, CONFIG_ITEM_STATUS.REQUESTED_VERSION.plus(1))
.set(CONFIG_ITEM_STATUS.REQUESTED_UPDATED, new Timestamp(System.currentTimeMillis()))
.where(
CONFIG_ITEM_STATUS.NAME.eq(itemName)
.and(targetObjectCondition(client))).execute();
return updated > 0;
}
protected RuntimeException apply(Client client, String itemName) {
try {
create()
.insertInto(CONFIG_ITEM_STATUS,
CONFIG_ITEM_STATUS.NAME,
getResourceField(client),
CONFIG_ITEM_STATUS.RESOURCE_TYPE,
CONFIG_ITEM_STATUS.RESOURCE_ID,
CONFIG_ITEM_STATUS.REQUESTED_VERSION,
CONFIG_ITEM_STATUS.REQUESTED_UPDATED)
.values(
itemName,
new Long(client.getResourceId()),
getResourceNameField(client),
new Long(client.getResourceId()),
1L,
new Timestamp(System.currentTimeMillis()))
.execute();
return null;
} catch ( DataAccessException e ) {
return e;
}
}
protected String getResourceNameField(Client client) {
return getResourceField(client).getName().toLowerCase();
}
protected TableField<ConfigItemStatusRecord, Long> getResourceField(Client client) {
if ( client.getResourceType() == Agent.class ) {
return CONFIG_ITEM_STATUS.AGENT_ID;
}
if ( client.getResourceType() == Service.class ) {
return CONFIG_ITEM_STATUS.SERVICE_ID;
}
if (client.getResourceType() == Stack.class) {
return CONFIG_ITEM_STATUS.STACK_ID;
}
if ( client.getResourceType() == Account.class ) {
return CONFIG_ITEM_STATUS.ACCOUNT_ID;
}
if (client.getResourceType() == Host.class) {
return CONFIG_ITEM_STATUS.HOST_ID;
}
throw new IllegalArgumentException("Unsupported client type [" + client.getResourceType() + "]");
}
@Override
public boolean isAssigned(Client client, String itemName) {
ConfigItemStatus status = create()
.selectFrom(CONFIG_ITEM_STATUS)
.where(
CONFIG_ITEM_STATUS.NAME.eq(itemName)
.and(targetObjectCondition(client)))
.fetchAny();
return status != null;
}
@Override
public boolean setApplied(Client client, String itemName, ItemVersion version) {
Context t = appliedTimer.time();
try {
int updated = update(CONFIG_ITEM_STATUS)
.set(CONFIG_ITEM_STATUS.APPLIED_VERSION, version.getRevision())
.set(CONFIG_ITEM_STATUS.SOURCE_VERSION, version.getSourceRevision())
.set(CONFIG_ITEM_STATUS.APPLIED_UPDATED, new Timestamp(System.currentTimeMillis()))
.where(
CONFIG_ITEM_STATUS.NAME.eq(itemName)
.and(targetObjectCondition(client)))
.execute();
if ( updated > 1 ) {
log.error("Updated too many rows [{}] for client [{}] itemName [{}] itemVersion [{}]",
updated, client, itemName, version);
}
if (updated == 1) {
ConfigUpdated event = new ConfigUpdated(client.getResourceType(), client.getResourceId(), itemName);
event.withResourceType(objectManager.getType(client.getResourceType())).withResourceId(Long.toString(client.getResourceId()));
DeferredUtils.deferPublish(eventService, event);
}
return false;
} finally {
t.stop();
}
}
@Override
public void setLatest(Client client, String itemName, String sourceRevision) {
update(CONFIG_ITEM_STATUS)
.set(CONFIG_ITEM_STATUS.APPLIED_VERSION, CONFIG_ITEM_STATUS.REQUESTED_VERSION)
.set(CONFIG_ITEM_STATUS.SOURCE_VERSION, sourceRevision)
.where(
CONFIG_ITEM_STATUS.NAME.eq(itemName)
.and(targetObjectCondition(client))
)
.execute();
}
protected Condition targetObjectCondition(Client client) {
return CONFIG_ITEM_STATUS.RESOURCE_TYPE.eq(getResourceNameField(client))
.and(CONFIG_ITEM_STATUS.RESOURCE_ID.eq(client.getResourceId()));
}
@Override
public void setItemSourceVersion(String name, String sourceRevision) {
ConfigItem item = create()
.selectFrom(CONFIG_ITEM)
.where(
CONFIG_ITEM.NAME.eq(name))
.fetchOne();
if ( item != null && sourceRevision.equals(item.getSourceVersion()) ) {
return;
}
log.info("Setting config [{}] to source version [{}]", name, sourceRevision);
int updated = create()
.update(CONFIG_ITEM)
.set(CONFIG_ITEM.SOURCE_VERSION, sourceRevision)
.where(
CONFIG_ITEM.NAME.eq(name))
.execute();
if ( updated == 0 ) {
create()
.insertInto(CONFIG_ITEM, CONFIG_ITEM.NAME, CONFIG_ITEM.SOURCE_VERSION)
.values(name, sourceRevision)
.execute();
}
}
@Override
public List<? extends ConfigItemStatus> listItems(ConfigUpdateRequest request) {
Set<String> names = new HashSet<String>();
for ( ConfigUpdateItem item : request.getItems() ) {
names.add(item.getName());
}
return create()
.selectFrom(CONFIG_ITEM_STATUS)
.where(
CONFIG_ITEM_STATUS.NAME.in(names)
.and(targetObjectCondition(request.getClient())))
.fetch();
}
@Override
public ItemVersion getRequestedItemVersion(Client client, String itemName) {
Record2<Long,String> result = create()
.select(CONFIG_ITEM_STATUS.REQUESTED_VERSION, CONFIG_ITEM.SOURCE_VERSION)
.from(CONFIG_ITEM_STATUS)
.join(CONFIG_ITEM)
.on(CONFIG_ITEM.NAME.eq(CONFIG_ITEM_STATUS.NAME))
.where(
CONFIG_ITEM_STATUS.NAME.eq(itemName)
.and(targetObjectCondition(client)))
.fetchOne();
return result == null ? null : new DefaultItemVersion(result.value1(), result.value2());
}
public ObjectManager getObjectManager() {
return objectManager;
}
@Inject
public void setObjectManager(ObjectManager objectManager) {
this.objectManager = objectManager;
}
@Override
public Map<Client, List<String>> findOutOfSync(boolean migration) {
Map<Client, List<String>> result = new HashMap<>();
for ( ConfigItemStatus status : (migration ? serviceMigrationItems() : serviceOutOfSyncItems()) ) {
Client client = new Client(status);
CollectionUtils.addToMap(result, client, status.getName(), ArrayList.class);
}
for (ConfigItemStatus status : (migration ? stackMigrationItems() : stackOutOfSyncItems())) {
Client client = new Client(status);
CollectionUtils.addToMap(result, client, status.getName(), ArrayList.class);
}
for ( ConfigItemStatus status : (migration ? agentMigrationItems() : agentOutOfSyncItems()) ) {
Client client = new Client(status);
CollectionUtils.addToMap(result, client, status.getName(), ArrayList.class);
}
for ( ConfigItemStatus status : (migration ? accountMigrationItems() : accountOutOfSyncItems()) ) {
Client client = new Client(status);
CollectionUtils.addToMap(result, client, status.getName(), ArrayList.class);
}
for (ConfigItemStatus status : (migration ? hostMigrationItems() : hostOutOfSyncItems())) {
Client client = new Client(status);
CollectionUtils.addToMap(result, client, status.getName(), ArrayList.class);
}
return result;
}
@Override
public Map<String, ItemVersion> getApplied(Client client) {
Map<String, ItemVersion> versions = new HashMap<>();
List<ConfigItemStatusRecord> records = create()
.selectFrom(CONFIG_ITEM_STATUS)
.where(targetObjectCondition(client))
.fetch();
for (ConfigItemStatusRecord record : records) {
Long applied = record.getAppliedVersion();
if (applied == null) {
continue;
}
versions.put(record.getName(), new DefaultItemVersion(record.getAppliedVersion(), ""));
}
return versions;
}
protected List<? extends ConfigItemStatus> serviceOutOfSyncItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(SERVICE)
.on(SERVICE.ID.eq(CONFIG_ITEM_STATUS.SERVICE_ID))
.where(CONFIG_ITEM_STATUS.REQUESTED_VERSION.ne(CONFIG_ITEM_STATUS.APPLIED_VERSION)
.and(SERVICE.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> stackOutOfSyncItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(STACK)
.on(STACK.ID.eq(CONFIG_ITEM_STATUS.STACK_ID))
.where(CONFIG_ITEM_STATUS.REQUESTED_VERSION.ne(CONFIG_ITEM_STATUS.APPLIED_VERSION)
.and(STACK.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> serviceMigrationItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(SERVICE)
.on(SERVICE.ID.eq(CONFIG_ITEM_STATUS.SERVICE_ID))
.join(CONFIG_ITEM)
.on(CONFIG_ITEM.NAME.eq(CONFIG_ITEM_STATUS.NAME))
.where(CONFIG_ITEM_STATUS.SOURCE_VERSION.isNotNull()
.and(CONFIG_ITEM_STATUS.SOURCE_VERSION.ne(CONFIG_ITEM.SOURCE_VERSION))
.and(SERVICE.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> hostMigrationItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(HOST)
.on(HOST.ID.eq(CONFIG_ITEM_STATUS.HOST_ID))
.join(CONFIG_ITEM)
.on(CONFIG_ITEM.NAME.eq(CONFIG_ITEM_STATUS.NAME))
.where(CONFIG_ITEM_STATUS.SOURCE_VERSION.isNotNull()
.and(CONFIG_ITEM_STATUS.SOURCE_VERSION.ne(CONFIG_ITEM.SOURCE_VERSION))
.and(HOST.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> stackMigrationItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(STACK)
.on(STACK.ID.eq(CONFIG_ITEM_STATUS.STACK_ID))
.join(CONFIG_ITEM)
.on(CONFIG_ITEM.NAME.eq(CONFIG_ITEM_STATUS.NAME))
.where(CONFIG_ITEM_STATUS.SOURCE_VERSION.isNotNull()
.and(CONFIG_ITEM_STATUS.SOURCE_VERSION.ne(CONFIG_ITEM.SOURCE_VERSION))
.and(STACK.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> accountOutOfSyncItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(ACCOUNT)
.on(ACCOUNT.ID.eq(CONFIG_ITEM_STATUS.ACCOUNT_ID))
.where(CONFIG_ITEM_STATUS.REQUESTED_VERSION.ne(CONFIG_ITEM_STATUS.APPLIED_VERSION)
.and(ACCOUNT.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> accountMigrationItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(ACCOUNT)
.on(ACCOUNT.ID.eq(CONFIG_ITEM_STATUS.ACCOUNT_ID))
.join(CONFIG_ITEM)
.on(CONFIG_ITEM.NAME.eq(CONFIG_ITEM_STATUS.NAME))
.where(CONFIG_ITEM_STATUS.SOURCE_VERSION.isNotNull()
.and(CONFIG_ITEM_STATUS.SOURCE_VERSION.ne(CONFIG_ITEM.SOURCE_VERSION))
.and(ACCOUNT.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> agentOutOfSyncItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(AGENT)
.on(AGENT.ID.eq(CONFIG_ITEM_STATUS.AGENT_ID))
.leftOuterJoin(INSTANCE)
.on(INSTANCE.AGENT_ID.eq(AGENT.ID))
.where(CONFIG_ITEM_STATUS.REQUESTED_VERSION.ne(CONFIG_ITEM_STATUS.APPLIED_VERSION)
.and(AGENT.STATE.in(CommonStatesConstants.ACTIVE, CommonStatesConstants.ACTIVATING,
AgentConstants.STATE_RECONNECTING, AgentConstants.STATE_FINISHING_RECONNECT,
AgentConstants.STATE_RECONNECTED))
.and(INSTANCE.STATE.isNull().or(INSTANCE.STATE.eq(InstanceConstants.STATE_RUNNING))))
.orderBy(AGENT.ID.asc())
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> hostOutOfSyncItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(HOST)
.on(HOST.ID.eq(CONFIG_ITEM_STATUS.HOST_ID))
.where(CONFIG_ITEM_STATUS.REQUESTED_VERSION.ne(CONFIG_ITEM_STATUS.APPLIED_VERSION)
.and(HOST.REMOVED.isNull()))
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
protected List<? extends ConfigItemStatus> agentMigrationItems() {
return create()
.select(CONFIG_ITEM_STATUS.fields())
.from(CONFIG_ITEM_STATUS)
.join(AGENT)
.on(AGENT.ID.eq(CONFIG_ITEM_STATUS.AGENT_ID))
.join(CONFIG_ITEM)
.on(CONFIG_ITEM.NAME.eq(CONFIG_ITEM_STATUS.NAME))
.leftOuterJoin(INSTANCE)
.on(INSTANCE.AGENT_ID.eq(AGENT.ID))
.where(CONFIG_ITEM_STATUS.SOURCE_VERSION.isNotNull()
.and(CONFIG_ITEM_STATUS.SOURCE_VERSION.ne(CONFIG_ITEM.SOURCE_VERSION))
.and(AGENT.STATE.in(CommonStatesConstants.ACTIVE, CommonStatesConstants.ACTIVATING,
AgentConstants.STATE_RECONNECTING, AgentConstants.STATE_FINISHING_RECONNECT,
AgentConstants.STATE_RECONNECTED))
.and(INSTANCE.STATE.isNull().or(INSTANCE.STATE.eq(InstanceConstants.STATE_RUNNING))))
.orderBy(AGENT.ID.asc())
.limit(BATCH_SIZE.get())
.fetchInto(ConfigItemStatusRecord.class);
}
@Override
public void setIfOlder(Client client, String itemName, Long version) {
create()
.update(CONFIG_ITEM_STATUS)
.set(CONFIG_ITEM_STATUS.REQUESTED_VERSION, version)
.set(CONFIG_ITEM_STATUS.REQUESTED_UPDATED, new Timestamp(System.currentTimeMillis()))
.where(
CONFIG_ITEM_STATUS.REQUESTED_VERSION.lt(version)
.and(CONFIG_ITEM_STATUS.NAME.eq(itemName))
.and(targetObjectCondition(client))).execute();
}
@Override
public void reset(String itemName, String version) {
create().update(CONFIG_ITEM_STATUS)
.set(CONFIG_ITEM_STATUS.APPLIED_VERSION, -1L)
.set(CONFIG_ITEM_STATUS.REQUESTED_VERSION, 0L)
.set(CONFIG_ITEM_STATUS.REQUESTED_UPDATED, new Date())
.where(CONFIG_ITEM_STATUS.NAME.eq(itemName)
.and(CONFIG_ITEM_STATUS.SOURCE_VERSION.ne(version)))
.execute();
}
}