diff --git a/Makefile b/Makefile index 771dbf89..48cf5004 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ deploy-demo: deploy kubectl apply -f ./deploy/samples/tabletriggers.yaml kubectl apply -f ./deploy/samples/crontrigger.yaml kubectl apply -f ./deploy/samples/user-jobs.yaml + kubectl apply -f ./deploy/samples/table-provisioning-job-template.yaml undeploy-demo: undeploy kubectl delete -f ./deploy/samples/demodb.yaml || echo "skipping" diff --git a/deploy/samples/table-provisioning-job-template.yaml b/deploy/samples/table-provisioning-job-template.yaml new file mode 100644 index 00000000..67cf3ed1 --- /dev/null +++ b/deploy/samples/table-provisioning-job-template.yaml @@ -0,0 +1,26 @@ +apiVersion: hoptimator.linkedin.com/v1alpha1 +kind: Database +metadata: + name: iceberg +spec: + catalog: ICEBERG + url: jdbc:demodb://names=ads + dialect: Calcite + +--- + +apiVersion: hoptimator.linkedin.com/v1alpha1 +kind: JobTemplate +metadata: + name: table-provisioning-job-template +spec: + databases: + - iceberg + yaml: | + apiVersion: hoptimator.linkedin.com/v1alpha1 + kind: TableProvisionJob + metadata: + name: {{name}}-provision-job + spec: + table: {{table}} + database: {{database}} \ No newline at end of file diff --git a/generate-models.sh b/generate-models.sh index 3c2a6d13..82c02877 100755 --- a/generate-models.sh +++ b/generate-models.sh @@ -16,6 +16,7 @@ docker run \ -u "$(pwd)/hoptimator-k8s/src/main/resources/pipelines.crd.yaml" \ -u "$(pwd)/hoptimator-k8s/src/main/resources/sqljobs.crd.yaml" \ -u "$(pwd)/hoptimator-k8s/src/main/resources/subscriptions.crd.yaml" \ + -u "$(pwd)/hoptimator-k8s/src/main/resources/tableprovisionjobs.crd.yaml" \ -u "$(pwd)/hoptimator-k8s/src/main/resources/tabletemplates.crd.yaml" \ -u "$(pwd)/hoptimator-k8s/src/main/resources/tabletriggers.crd.yaml" \ -u "$(pwd)/hoptimator-k8s/src/main/resources/views.crd.yaml" \ diff --git a/hoptimator-k8s/generate-models.sh b/hoptimator-k8s/generate-models.sh index 462e41ea..6d97b208 100755 --- a/hoptimator-k8s/generate-models.sh +++ b/hoptimator-k8s/generate-models.sh @@ -10,5 +10,5 @@ docker run \ --network host \ ghcr.io/kubernetes-client/java/crd-model-gen:v1.0.6 \ /generate.sh -o "$(pwd)" -n "" -p "com.linkedin.hoptimator.k8s" \ - -u "$(pwd)/src/main/resources/tabletriggers.crd.yaml" \ + -u "$(pwd)/src/main/resources/tableprovisionjobs.crd.yaml" \ && echo "done." diff --git a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sApiEndpoints.java b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sApiEndpoints.java index ff5fcccb..ba9aaa18 100644 --- a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sApiEndpoints.java +++ b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sApiEndpoints.java @@ -17,6 +17,8 @@ import com.linkedin.hoptimator.k8s.models.V1alpha1JobTemplateList; import com.linkedin.hoptimator.k8s.models.V1alpha1Pipeline; import com.linkedin.hoptimator.k8s.models.V1alpha1PipelineList; +import com.linkedin.hoptimator.k8s.models.V1alpha1TableProvisionJob; +import com.linkedin.hoptimator.k8s.models.V1alpha1TableProvisionJobList; import com.linkedin.hoptimator.k8s.models.V1alpha1TableTemplate; import com.linkedin.hoptimator.k8s.models.V1alpha1TableTemplateList; import com.linkedin.hoptimator.k8s.models.V1alpha1TableTrigger; @@ -58,6 +60,9 @@ public final class K8sApiEndpoints { public static final K8sApiEndpoint JOB_TEMPLATES = new K8sApiEndpoint<>("JobTemplate", "hoptimator.linkedin.com", "v1alpha1", "jobtemplates", false, V1alpha1JobTemplate.class, V1alpha1JobTemplateList.class); + public static final K8sApiEndpoint TABLE_PROVISION_JOBS = + new K8sApiEndpoint<>("TableProvisionJob", "hoptimator.linkedin.com", "v1alpha1", + "tableprovisionjobs", false, V1alpha1TableProvisionJob.class, V1alpha1TableProvisionJobList.class); public static final K8sApiEndpoint TABLE_TRIGGERS = new K8sApiEndpoint<>("TableTrigger", "hoptimator.linkedin.com", "v1alpha1", "tabletriggers", false, V1alpha1TableTrigger.class, V1alpha1TableTriggerList.class); diff --git a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sDeployerProvider.java b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sDeployerProvider.java index bf66193a..c7af311e 100644 --- a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sDeployerProvider.java +++ b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sDeployerProvider.java @@ -3,14 +3,20 @@ import java.sql.Connection; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import com.linkedin.hoptimator.Deployable; import com.linkedin.hoptimator.Deployer; import com.linkedin.hoptimator.DeployerProvider; import com.linkedin.hoptimator.Job; import com.linkedin.hoptimator.MaterializedView; +import com.linkedin.hoptimator.Sink; import com.linkedin.hoptimator.Source; +import com.linkedin.hoptimator.SqlDialect; +import com.linkedin.hoptimator.ThrowingFunction; import com.linkedin.hoptimator.Trigger; import com.linkedin.hoptimator.View; @@ -29,6 +35,11 @@ public Collection deployers(T obj, Connection c list.add(new K8sJobDeployer((Job) obj, context)); } else if (obj instanceof Source) { list.add(new K8sSourceDeployer((Source) obj, context)); + if (!(obj instanceof Sink)) { + // Sets up a table provisioning job for the source. + // The job would be a no-op if the source is already provisioned. + list.add(new K8sJobDeployer(jobFromSource((Source) obj), context)); + } } else if (obj instanceof Trigger) { list.add(new K8sTriggerDeployer((Trigger) obj, context)); } @@ -36,6 +47,15 @@ public Collection deployers(T obj, Connection c return list; } + private static Job jobFromSource(Source source) { + Sink sink = new Sink(source.database(), source.path(), source.options()); + Map> lazyEvals = new HashMap<>(); + lazyEvals.put("sql", dialect -> null); + lazyEvals.put("query", dialect -> null); + lazyEvals.put("fieldMap", dialect -> null); + return new Job(source.table(), Collections.emptySet(), sink, lazyEvals); + } + @Override public int priority() { return 1; diff --git a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sJobDeployer.java b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sJobDeployer.java index 491c1a4b..4222f50c 100644 --- a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sJobDeployer.java +++ b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/K8sJobDeployer.java @@ -55,6 +55,10 @@ public List specify() throws SQLException { .with("flinksql", () -> sql.apply(SqlDialect.FLINK)) .with("flinkconfigs", properties) .with("fieldMap", () -> "'" + fieldMap.apply(SqlDialect.ANSI) + "'") + .with("fieldMap", () -> { + String raw = fieldMap.apply(SqlDialect.ANSI); + return raw != null ? "'" + raw + "'" : null; + }) .with(properties); List templates = jobTemplateApi.list() .stream() diff --git a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJob.java b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJob.java new file mode 100644 index 00000000..01f78102 --- /dev/null +++ b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJob.java @@ -0,0 +1,219 @@ +/* + * Kubernetes + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: v1.21.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.linkedin.hoptimator.k8s.models; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.linkedin.hoptimator.k8s.models.V1alpha1TableProvisionJobSpec; +import com.linkedin.hoptimator.k8s.models.V1alpha1TableProvisionJobStatus; +import io.kubernetes.client.openapi.models.V1ObjectMeta; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; + +/** + * Job to provision backing infrastructure for a table. + */ +@ApiModel(description = "Job to provision backing infrastructure for a table.") +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-13T23:58:00.641Z[Etc/UTC]") +public class V1alpha1TableProvisionJob implements io.kubernetes.client.common.KubernetesObject { + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion; + + public static final String SERIALIZED_NAME_KIND = "kind"; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + + public static final String SERIALIZED_NAME_METADATA = "metadata"; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ObjectMeta metadata = null; + + public static final String SERIALIZED_NAME_SPEC = "spec"; + @SerializedName(SERIALIZED_NAME_SPEC) + private V1alpha1TableProvisionJobSpec spec; + + public static final String SERIALIZED_NAME_STATUS = "status"; + @SerializedName(SERIALIZED_NAME_STATUS) + private V1alpha1TableProvisionJobStatus status; + + + public V1alpha1TableProvisionJob apiVersion(String apiVersion) { + + this.apiVersion = apiVersion; + return this; + } + + /** + * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + * @return apiVersion + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources") + + public String getApiVersion() { + return apiVersion; + } + + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + + public V1alpha1TableProvisionJob kind(String kind) { + + this.kind = kind; + return this; + } + + /** + * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + * @return kind + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds") + + public String getKind() { + return kind; + } + + + public void setKind(String kind) { + this.kind = kind; + } + + + public V1alpha1TableProvisionJob metadata(V1ObjectMeta metadata) { + + this.metadata = metadata; + return this; + } + + /** + * Get metadata + * @return metadata + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public V1ObjectMeta getMetadata() { + return metadata; + } + + + public void setMetadata(V1ObjectMeta metadata) { + this.metadata = metadata; + } + + + public V1alpha1TableProvisionJob spec(V1alpha1TableProvisionJobSpec spec) { + + this.spec = spec; + return this; + } + + /** + * Get spec + * @return spec + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public V1alpha1TableProvisionJobSpec getSpec() { + return spec; + } + + + public void setSpec(V1alpha1TableProvisionJobSpec spec) { + this.spec = spec; + } + + + public V1alpha1TableProvisionJob status(V1alpha1TableProvisionJobStatus status) { + + this.status = status; + return this; + } + + /** + * Get status + * @return status + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public V1alpha1TableProvisionJobStatus getStatus() { + return status; + } + + + public void setStatus(V1alpha1TableProvisionJobStatus status) { + this.status = status; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1alpha1TableProvisionJob v1alpha1TableProvisionJob = (V1alpha1TableProvisionJob) o; + return Objects.equals(this.apiVersion, v1alpha1TableProvisionJob.apiVersion) && + Objects.equals(this.kind, v1alpha1TableProvisionJob.kind) && + Objects.equals(this.metadata, v1alpha1TableProvisionJob.metadata) && + Objects.equals(this.spec, v1alpha1TableProvisionJob.spec) && + Objects.equals(this.status, v1alpha1TableProvisionJob.status); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, kind, metadata, spec, status); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class V1alpha1TableProvisionJob {\n"); + sb.append(" apiVersion: ").append(toIndentedString(apiVersion)).append("\n"); + sb.append(" kind: ").append(toIndentedString(kind)).append("\n"); + sb.append(" metadata: ").append(toIndentedString(metadata)).append("\n"); + sb.append(" spec: ").append(toIndentedString(spec)).append("\n"); + sb.append(" status: ").append(toIndentedString(status)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobList.java b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobList.java new file mode 100644 index 00000000..237c4ac8 --- /dev/null +++ b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobList.java @@ -0,0 +1,195 @@ +/* + * Kubernetes + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: v1.21.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.linkedin.hoptimator.k8s.models; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import com.linkedin.hoptimator.k8s.models.V1alpha1TableProvisionJob; +import io.kubernetes.client.openapi.models.V1ListMeta; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * TableProvisionJobList is a list of TableProvisionJob + */ +@ApiModel(description = "TableProvisionJobList is a list of TableProvisionJob") +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-13T23:58:00.641Z[Etc/UTC]") +public class V1alpha1TableProvisionJobList implements io.kubernetes.client.common.KubernetesListObject { + public static final String SERIALIZED_NAME_API_VERSION = "apiVersion"; + @SerializedName(SERIALIZED_NAME_API_VERSION) + private String apiVersion; + + public static final String SERIALIZED_NAME_ITEMS = "items"; + @SerializedName(SERIALIZED_NAME_ITEMS) + private List items = new ArrayList<>(); + + public static final String SERIALIZED_NAME_KIND = "kind"; + @SerializedName(SERIALIZED_NAME_KIND) + private String kind; + + public static final String SERIALIZED_NAME_METADATA = "metadata"; + @SerializedName(SERIALIZED_NAME_METADATA) + private V1ListMeta metadata = null; + + + public V1alpha1TableProvisionJobList apiVersion(String apiVersion) { + + this.apiVersion = apiVersion; + return this; + } + + /** + * APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + * @return apiVersion + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources") + + public String getApiVersion() { + return apiVersion; + } + + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + + public V1alpha1TableProvisionJobList items(List items) { + + this.items = items; + return this; + } + + public V1alpha1TableProvisionJobList addItemsItem(V1alpha1TableProvisionJob itemsItem) { + this.items.add(itemsItem); + return this; + } + + /** + * List of tableprovisionjobs. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md + * @return items + **/ + @ApiModelProperty(required = true, value = "List of tableprovisionjobs. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md") + + public List getItems() { + return items; + } + + + public void setItems(List items) { + this.items = items; + } + + + public V1alpha1TableProvisionJobList kind(String kind) { + + this.kind = kind; + return this; + } + + /** + * Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + * @return kind + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds") + + public String getKind() { + return kind; + } + + + public void setKind(String kind) { + this.kind = kind; + } + + + public V1alpha1TableProvisionJobList metadata(V1ListMeta metadata) { + + this.metadata = metadata; + return this; + } + + /** + * Get metadata + * @return metadata + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "") + + public V1ListMeta getMetadata() { + return metadata; + } + + + public void setMetadata(V1ListMeta metadata) { + this.metadata = metadata; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1alpha1TableProvisionJobList v1alpha1TableProvisionJobList = (V1alpha1TableProvisionJobList) o; + return Objects.equals(this.apiVersion, v1alpha1TableProvisionJobList.apiVersion) && + Objects.equals(this.items, v1alpha1TableProvisionJobList.items) && + Objects.equals(this.kind, v1alpha1TableProvisionJobList.kind) && + Objects.equals(this.metadata, v1alpha1TableProvisionJobList.metadata); + } + + @Override + public int hashCode() { + return Objects.hash(apiVersion, items, kind, metadata); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class V1alpha1TableProvisionJobList {\n"); + sb.append(" apiVersion: ").append(toIndentedString(apiVersion)).append("\n"); + sb.append(" items: ").append(toIndentedString(items)).append("\n"); + sb.append(" kind: ").append(toIndentedString(kind)).append("\n"); + sb.append(" metadata: ").append(toIndentedString(metadata)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobSpec.java b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobSpec.java new file mode 100644 index 00000000..247b92b2 --- /dev/null +++ b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobSpec.java @@ -0,0 +1,127 @@ +/* + * Kubernetes + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: v1.21.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.linkedin.hoptimator.k8s.models; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; + +/** + * TableProvisionJob spec. + */ +@ApiModel(description = "TableProvisionJob spec.") +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-13T23:58:00.641Z[Etc/UTC]") +public class V1alpha1TableProvisionJobSpec { + public static final String SERIALIZED_NAME_DATABASE = "database"; + @SerializedName(SERIALIZED_NAME_DATABASE) + private String database; + + public static final String SERIALIZED_NAME_TABLE = "table"; + @SerializedName(SERIALIZED_NAME_TABLE) + private String table; + + + public V1alpha1TableProvisionJobSpec database(String database) { + + this.database = database; + return this; + } + + /** + * Database the table belongs to. + * @return database + **/ + @ApiModelProperty(required = true, value = "Database the table belongs to.") + + public String getDatabase() { + return database; + } + + + public void setDatabase(String database) { + this.database = database; + } + + + public V1alpha1TableProvisionJobSpec table(String table) { + + this.table = table; + return this; + } + + /** + * Table name to provision. + * @return table + **/ + @ApiModelProperty(required = true, value = "Table name to provision.") + + public String getTable() { + return table; + } + + + public void setTable(String table) { + this.table = table; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1alpha1TableProvisionJobSpec v1alpha1TableProvisionJobSpec = (V1alpha1TableProvisionJobSpec) o; + return Objects.equals(this.database, v1alpha1TableProvisionJobSpec.database) && + Objects.equals(this.table, v1alpha1TableProvisionJobSpec.table); + } + + @Override + public int hashCode() { + return Objects.hash(database, table); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class V1alpha1TableProvisionJobSpec {\n"); + sb.append(" database: ").append(toIndentedString(database)).append("\n"); + sb.append(" table: ").append(toIndentedString(table)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobStatus.java b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobStatus.java new file mode 100644 index 00000000..516a0336 --- /dev/null +++ b/hoptimator-k8s/src/main/java/com/linkedin/hoptimator/k8s/models/V1alpha1TableProvisionJobStatus.java @@ -0,0 +1,158 @@ +/* + * Kubernetes + * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator) + * + * The version of the OpenAPI document: v1.21.1 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package com.linkedin.hoptimator.k8s.models; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.IOException; + +/** + * Filled in by the operator. + */ +@ApiModel(description = "Filled in by the operator.") +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2026-03-13T23:58:00.641Z[Etc/UTC]") +public class V1alpha1TableProvisionJobStatus { + public static final String SERIALIZED_NAME_FAILED = "failed"; + @SerializedName(SERIALIZED_NAME_FAILED) + private Boolean failed; + + public static final String SERIALIZED_NAME_MESSAGE = "message"; + @SerializedName(SERIALIZED_NAME_MESSAGE) + private String message; + + public static final String SERIALIZED_NAME_READY = "ready"; + @SerializedName(SERIALIZED_NAME_READY) + private Boolean ready; + + + public V1alpha1TableProvisionJobStatus failed(Boolean failed) { + + this.failed = failed; + return this; + } + + /** + * Whether the provisioning has failed. + * @return failed + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Whether the provisioning has failed.") + + public Boolean getFailed() { + return failed; + } + + + public void setFailed(Boolean failed) { + this.failed = failed; + } + + + public V1alpha1TableProvisionJobStatus message(String message) { + + this.message = message; + return this; + } + + /** + * Error or success message, for information only. + * @return message + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Error or success message, for information only.") + + public String getMessage() { + return message; + } + + + public void setMessage(String message) { + this.message = message; + } + + + public V1alpha1TableProvisionJobStatus ready(Boolean ready) { + + this.ready = ready; + return this; + } + + /** + * Whether the table has been provisioned. + * @return ready + **/ + @javax.annotation.Nullable + @ApiModelProperty(value = "Whether the table has been provisioned.") + + public Boolean getReady() { + return ready; + } + + + public void setReady(Boolean ready) { + this.ready = ready; + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + V1alpha1TableProvisionJobStatus v1alpha1TableProvisionJobStatus = (V1alpha1TableProvisionJobStatus) o; + return Objects.equals(this.failed, v1alpha1TableProvisionJobStatus.failed) && + Objects.equals(this.message, v1alpha1TableProvisionJobStatus.message) && + Objects.equals(this.ready, v1alpha1TableProvisionJobStatus.ready); + } + + @Override + public int hashCode() { + return Objects.hash(failed, message, ready); + } + + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class V1alpha1TableProvisionJobStatus {\n"); + sb.append(" failed: ").append(toIndentedString(failed)).append("\n"); + sb.append(" message: ").append(toIndentedString(message)).append("\n"); + sb.append(" ready: ").append(toIndentedString(ready)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + +} + diff --git a/hoptimator-k8s/src/main/resources/tableprovisionjobs.crd.yaml b/hoptimator-k8s/src/main/resources/tableprovisionjobs.crd.yaml new file mode 100644 index 00000000..21cec77e --- /dev/null +++ b/hoptimator-k8s/src/main/resources/tableprovisionjobs.crd.yaml @@ -0,0 +1,71 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: tableprovisionjobs.hoptimator.linkedin.com +spec: + group: hoptimator.linkedin.com + names: + kind: TableProvisionJob + listKind: TableProvisionJobList + plural: tableprovisionjobs + singular: tableprovisionjob + shortNames: + - tpj + preserveUnknownFields: false + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + schema: + openAPIV3Schema: + description: Job to provision backing infrastructure for a table. + type: object + properties: + apiVersion: + type: string + kind: + type: string + metadata: + type: object + spec: + description: TableProvisionJob spec. + type: object + properties: + database: + description: Database the table belongs to. + type: string + table: + description: Table name to provision. + type: string + required: + - database + - table + status: + description: Filled in by the operator. + type: object + properties: + ready: + description: Whether the table has been provisioned. + type: boolean + failed: + description: Whether the provisioning has failed. + type: boolean + message: + description: Error or success message, for information only. + type: string + subresources: + status: {} + additionalPrinterColumns: + - name: DATABASE + type: string + description: Database. + jsonPath: .spec.database + - name: TABLE + type: string + description: Table. + jsonPath: .spec.table + - name: STATUS + type: string + description: Job status. + jsonPath: .status.message