ca.vanzyl.concord.k8s.K8sHelper Maven / Gradle / Ivy
The newest version!
package ca.vanzyl.concord.k8s;
import static ca.vanzyl.concord.k8s.db.DBUtils.fromJSONB;
import static ca.vanzyl.concord.k8s.db.DBUtils.toJSONB;
import static ca.vanzyl.concord.k8s.jooq.Tables.K8S_CLUSTERS;
import static ca.vanzyl.concord.k8s.jooq.Tables.K8S_CLUSTER_DEPLOYMENTS;
import static org.jooq.impl.DSL.currentOffsetDateTime;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.value;
import ca.vanzyl.concord.k8s.inventory.K8sInventoryClusterEntry;
import ca.vanzyl.concord.k8s.jooq.tables.K8sClusterDeployments;
import ca.vanzyl.concord.k8s.jooq.tables.K8sClusters;
import ca.vanzyl.concord.k8s.model.K8sCluster;
import ca.vanzyl.concord.k8s.model.K8sClusterDeployment;
import ca.vanzyl.concord.k8s.model.K8sClusterDeploymentList;
import ca.vanzyl.concord.k8s.model.K8sClusterList;
import ca.vanzyl.concord.k8s.model.Metadata;
import ca.vanzyl.concord.k8s.model.aws.Aws;
import com.walmartlabs.concord.db.AbstractDao;
import com.walmartlabs.concord.db.MainDB;
import com.walmartlabs.concord.server.sdk.ConcordApplicationException;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.UnaryOperator;
import javax.inject.Inject;
import javax.inject.Named;
import javax.ws.rs.core.Response;
import org.jooq.Configuration;
import org.jooq.Record1;
@Named
public class K8sHelper
extends AbstractDao {
private static final int MAX_OPTIMISTIC_TRIES = 3;
@Inject
public K8sHelper(@MainDB Configuration cfg) {
super(cfg);
}
private static Metadata updateTimestamp(Metadata metadata, OffsetDateTime createdAt, OffsetDateTime updatedAt) {
// TODO(ib): convert to UTC for consistency in the json output?
return Metadata.builder()
.from(metadata)
.createdAt(createdAt)
.updatedAt(updatedAt)
.build();
}
private static K8sClusterDeployment filterOutSensitiveData(K8sClusterDeployment deployment) {
Aws aws = deployment.aws();
if (aws == null) {
return deployment;
}
return K8sClusterDeployment.builder()
.from(deployment)
.aws(Aws.builder()
.from(aws)
.accessKeyId(null)
.secretAccessKey(null)
.build())
.build();
}
public K8sClusterDeployment insertClusterDeployment(UUID orgId, K8sClusterDeployment deployment) {
OffsetDateTime now = OffsetDateTime.now();
K8sClusterDeployment effectiveDeployment = K8sClusterDeployment.builder()
.from(deployment)
.metadata(updateTimestamp(deployment.metadata(), now, now))
.build();
K8sClusterDeployments t = K8S_CLUSTER_DEPLOYMENTS;
return txResult(tx -> {
Record1 r = tx.insertInto(t).columns(t.ORG_ID, t.CLUSTER_ID, t.VALUE, t.CREATED_AT, t.UPDATED_AT)
.values(orgId, effectiveDeployment.metadata().name(), toJSONB(filterOutSensitiveData(effectiveDeployment)), now, now)
.returningResult(t.DEPLOYMENT_ID)
.fetchOne();
return K8sClusterDeployment.builder()
.from(effectiveDeployment)
.deploymentId(r.value1())
.build();
});
}
public void updateClusterDeployment(UUID deploymentId, K8sClusterDeployment deployment) {
K8sClusterDeployments t = K8S_CLUSTER_DEPLOYMENTS;
tx(tx -> tx.update(t)
.set(t.UPDATED_AT, currentOffsetDateTime())
.set(t.VALUE, toJSONB(filterOutSensitiveData(deployment)))
.where(t.DEPLOYMENT_ID.eq(deploymentId))
.execute());
}
public K8sCluster insertCluster(UUID deploymentId, K8sCluster cluster) {
OffsetDateTime now = OffsetDateTime.now();
K8sCluster effectiveCluster = K8sCluster.builder()
.from(cluster)
.metadata(updateTimestamp(cluster.metadata(), now, now))
.build();
K8sClusters t = K8S_CLUSTERS;
return txResult(tx -> {
tx.insertInto(t).columns(t.DEPLOYMENT_ID, t.VALUE, t.CREATED_AT, t.UPDATED_AT)
.values(deploymentId, toJSONB(effectiveCluster), now, now)
.execute();
return effectiveCluster;
});
}
public Optional getClusterDeployment(UUID orgId, String clusterId) {
K8sClusterDeployments t = K8S_CLUSTER_DEPLOYMENTS.as("kcd");
return dsl().select(t.DEPLOYMENT_ID, t.VALUE)
.from(t)
.where(t.ORG_ID.eq(orgId).and(t.CLUSTER_ID.eq(clusterId)))
.fetchOptional(r -> K8sClusterDeployment.builder()
.from(fromJSONB(r.value2(), K8sClusterDeployment.class))
.deploymentId(r.value1())
.build());
}
public Optional getClusterDeployment(UUID deploymentId) {
K8sClusterDeployments t = K8S_CLUSTER_DEPLOYMENTS.as("kcd");
return dsl().select(t.DEPLOYMENT_ID, t.VALUE)
.from(t)
.where(t.DEPLOYMENT_ID.eq(deploymentId))
.fetchOptional(r -> K8sClusterDeployment.builder()
.from(fromJSONB(r.value2(), K8sClusterDeployment.class))
.deploymentId(r.value1())
.build());
}
// TODO(ib): create a filter object
public K8sClusterDeploymentList listClusterDeployments(UUID orgId, String clusterId) {
K8sClusterDeployments t = K8S_CLUSTER_DEPLOYMENTS.as("kcd");
List deploymentIds = dsl().select(t.DEPLOYMENT_ID)
.from(t)
.where(t.ORG_ID.eq(orgId)
.and(value(clusterId).isNull()
.or(t.CLUSTER_ID.eq(clusterId))))
.fetch(r -> r.get(t.DEPLOYMENT_ID));
return K8sClusterDeploymentList.builder()
.deploymentIds(deploymentIds)
.build();
}
public K8sClusterList listClusters(UUID orgId) {
K8sClusters kc = K8S_CLUSTERS.as("kc");
K8sClusterDeployments kd = K8S_CLUSTER_DEPLOYMENTS.as("kcd");
List clusterIds = dsl().select(kd.CLUSTER_ID)
.from(kc, kd)
.where(kc.DEPLOYMENT_ID.eq(kd.DEPLOYMENT_ID)
.and(kd.ORG_ID.eq(orgId)))
.fetch(r -> r.get(kd.CLUSTER_ID));
return K8sClusterList.builder()
.clusterIds(clusterIds)
.build();
}
public Optional getCluster(UUID deploymentId) {
K8sClusters t = K8S_CLUSTERS.as("kc");
return dsl().select(t.VALUE)
.from(t)
.where(t.DEPLOYMENT_ID.eq(deploymentId))
.fetchOptional(r -> fromJSONB(r.value1(), K8sCluster.class));
}
public Optional getCluster(UUID orgId, String clusterId) {
K8sClusters kc = K8S_CLUSTERS.as("kc");
K8sClusterDeployments kd = K8S_CLUSTER_DEPLOYMENTS.as("kd");
return dsl().select(kc.VALUE)
.from(kc, kd)
.where(kc.DEPLOYMENT_ID.eq(kd.DEPLOYMENT_ID)
.and(kd.ORG_ID.eq(orgId))
.and(kd.CLUSTER_ID.eq(clusterId)))
.fetchOptional(r -> fromJSONB(r.value1(), K8sCluster.class));
}
public K8sCluster updateCluster(UUID orgId, String clusterId, UnaryOperator updater) {
OffsetDateTime expectedUpdatedAt = null;
int tryNumber = 0;
while (tryNumber++ < MAX_OPTIMISTIC_TRIES) {
Optional currentValue = getCluster(orgId, clusterId);
if (currentValue.isEmpty()) {
throw new ConcordApplicationException(String.format("Cluster not found: %s", clusterId), Response.Status.NOT_FOUND);
}
K8sCluster cluster = currentValue.get();
expectedUpdatedAt = cluster.metadata().updatedAt();
cluster = updater.apply(currentValue.get());
currentValue = updateCluster(cluster, expectedUpdatedAt);
if (currentValue.isPresent()) {
return currentValue.get();
}
}
throw new ConcordApplicationException(String.format("Optimistic locking timeout while updating a cluster: %s (expected updatedAt=%s)", clusterId, expectedUpdatedAt),
Response.Status.INTERNAL_SERVER_ERROR);
}
// TODO(ib): do we really need both methods?
public K8sCluster updateCluster(UUID deploymentId, Function> updater) {
OffsetDateTime expectedUpdatedAt = null;
int tryNumber = 0;
while (tryNumber++ < MAX_OPTIMISTIC_TRIES) {
Optional currentValue = getCluster(deploymentId);
if (currentValue.isEmpty()) {
throw new ConcordApplicationException(String.format("Cluster not found (deploymentId=%s)", deploymentId), Response.Status.NOT_FOUND);
}
K8sCluster cluster = currentValue.get();
expectedUpdatedAt = cluster.metadata().updatedAt();
Optional updatedCluster = updater.apply(currentValue.get());
if (updatedCluster.isEmpty()) {
return cluster;
}
cluster = updatedCluster.get();
currentValue = updateCluster(cluster, expectedUpdatedAt);
if (currentValue.isPresent()) {
return currentValue.get();
}
}
throw new ConcordApplicationException(String.format("Optimistic locking timeout while updating a cluster (deploymentId=%s, expected updatedAt=%s)", deploymentId, expectedUpdatedAt),
Response.Status.INTERNAL_SERVER_ERROR);
}
public Optional updateCluster(K8sCluster cluster, OffsetDateTime expectedUpdatedAt) {
OffsetDateTime now = OffsetDateTime.now();
K8sCluster effectiveCluster = K8sCluster.builder()
.from(cluster)
.metadata(updateTimestamp(cluster.metadata(), cluster.metadata().createdAt(), now))
.build();
K8sClusters kc = K8S_CLUSTERS.as("kc");
return txResult(tx -> {
int i = tx.update(kc)
.set(kc.UPDATED_AT, now)
.set(kc.VALUE, toJSONB(effectiveCluster))
.where(kc.DEPLOYMENT_ID.eq(cluster.deploymentId())
.and(kc.UPDATED_AT.eq(expectedUpdatedAt)))
.execute();
return i == 1 ? Optional.of(effectiveCluster) : Optional.empty();
});
}
public boolean deleteCluster(UUID orgId, String clusterId) {
K8sClusters kc = K8S_CLUSTERS.as("kc");
K8sClusterDeployments kd = K8S_CLUSTER_DEPLOYMENTS.as("kd");
return txResult(tx -> tx.deleteFrom(kc)
.where(kc.DEPLOYMENT_ID.eq(select(kd.DEPLOYMENT_ID)
.from(kd)
.where(kd.ORG_ID.eq(orgId)
.and(kd.CLUSTER_ID.eq(clusterId)))))
.execute() == 1);
}
public Collection pollClusterEntries() {
K8sClusters kc = K8S_CLUSTERS.as("kc");
K8sClusterDeployments kd = K8S_CLUSTER_DEPLOYMENTS.as("kd");
return dsl().select(kd.ORG_ID, kd.CLUSTER_ID)
.from(kc, kd)
.where(kc.DEPLOYMENT_ID.eq(kd.DEPLOYMENT_ID))
.fetch(r -> K8sInventoryClusterEntry.builder()
.orgId(r.get(kd.ORG_ID))
.clusterId(r.get(kd.CLUSTER_ID))
.build());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy