diff --git a/docs/data-sources/app.md b/docs/data-sources/app.md
index 2ffce32..47c1bd4 100644
--- a/docs/data-sources/app.md
+++ b/docs/data-sources/app.md
@@ -66,7 +66,6 @@ output "app_api_key" {
### Read-Only
-- `api_key` (String, Sensitive) The API key for the app.
- `cluster_id` (String) The Kubernetes cluster identifier where the app is deployed.
- `cname` (String) The region identifier (cname) for the app.
- `created_at` (String) The timestamp when the app was created.
diff --git a/docs/data-sources/apps.md b/docs/data-sources/apps.md
index ac6b783..9c781ce 100644
--- a/docs/data-sources/apps.md
+++ b/docs/data-sources/apps.md
@@ -84,7 +84,6 @@ output "found_app_id" {
Read-Only:
-- `api_key` (String, Sensitive) The API key for the app.
- `cluster_id` (String) The Kubernetes cluster identifier where the app is deployed.
- `cname` (String) The region identifier (cname) for the app.
- `created_at` (String) The timestamp when the app was created.
diff --git a/docs/resources/app.md b/docs/resources/app.md
index 59ebacc..2e93683 100644
--- a/docs/resources/app.md
+++ b/docs/resources/app.md
@@ -158,6 +158,7 @@ resource "spiceai_app" "with_region_lookup" {
### Optional
- `description` (String) A description of the app.
+- `executor` (Attributes) Executor container configuration. (see [below for nested schema](#nestedatt--executor))
- `image` (String) Image name for the spiced container.
- `image_tag` (String) The Spice.ai runtime image tag to use for deployments (e.g., `latest`, `v0.18.0`).
- `node_group` (String) The node group for the app deployment.
@@ -165,19 +166,82 @@ resource "spiceai_app" "with_region_lookup" {
- `region` (String) The region for the app deployment.
- `registry` (String) Registry for the spiced image.
- `replicas` (Number) The number of replicas for the app. Must be between 0 and 10.
+- `resources` (Attributes) Resource requests and limits for the app container. (see [below for nested schema](#nestedatt--resources))
- `spicepod` (String) The spicepod configuration as a YAML or JSON string. This defines the datasets, models, and other spicepod settings for the app.
- `storage_claim_size_gb` (Number) The storage claim size in GB for the app.
- `tags` (Map of String) Key-value tags for the app.
-- `update_channel` (String) Update channel for the spicepod. Valid values are `stable`, `nightly`, `internal`, `internal-sandbox`.
+- `update_channel` (String) Update channel for the spicepod. Valid values are `stable`, `preview`, `nightly`, and `internal`.
- `visibility` (String) The visibility of the app. Valid values are `public` or `private`. Defaults to `private`.
### Read-Only
-- `api_key` (String, Sensitive) The API key for the app. This is used to authenticate requests to the app's endpoints.
- `cluster_id` (String) The Kubernetes cluster identifier where the app is deployed.
- `created_at` (String) The timestamp when the app was created.
- `id` (String) The unique identifier of the app.
+
+### Nested Schema for `executor`
+
+Optional:
+
+- `replicas` (Number) Number of executor replicas.
+- `resources` (Attributes) (see [below for nested schema](#nestedatt--executor--resources))
+
+
+### Nested Schema for `executor.resources`
+
+Optional:
+
+- `limits` (Attributes) (see [below for nested schema](#nestedatt--executor--resources--limits))
+- `requests` (Attributes) (see [below for nested schema](#nestedatt--executor--resources--requests))
+
+
+### Nested Schema for `executor.resources.limits`
+
+Optional:
+
+- `cpu` (String) Whole-number vCPU limit, or `-` for no CPU limit.
+- `ephemeral_storage` (String) Ephemeral storage limit in Gi (for example, `8Gi`).
+- `memory` (String) Memory limit in Gi (for example, `16Gi`).
+
+
+
+### Nested Schema for `executor.resources.requests`
+
+Optional:
+
+- `cpu` (String)
+- `memory` (String)
+
+
+
+
+
+### Nested Schema for `resources`
+
+Optional:
+
+- `limits` (Attributes) (see [below for nested schema](#nestedatt--resources--limits))
+- `requests` (Attributes) (see [below for nested schema](#nestedatt--resources--requests))
+
+
+### Nested Schema for `resources.limits`
+
+Optional:
+
+- `cpu` (String) Whole-number vCPU limit, or `-` for no CPU limit.
+- `ephemeral_storage` (String) Ephemeral storage limit in Gi (for example, `8Gi`).
+- `memory` (String) Memory limit in Gi (for example, `16Gi`).
+
+
+
+### Nested Schema for `resources.requests`
+
+Optional:
+
+- `cpu` (String)
+- `memory` (String)
+
## Import
Import is supported using the following syntax:
diff --git a/docs/resources/deployment.md b/docs/resources/deployment.md
index 3121e5d..1cbc8d4 100644
--- a/docs/resources/deployment.md
+++ b/docs/resources/deployment.md
@@ -138,6 +138,7 @@ resource "spiceai_deployment" "production" {
### Optional
- `branch` (String) Git branch name associated with this deployment. Changing this forces a new deployment to be created.
+- `channel` (String) Override the deployment channel for this deployment. Valid values are `stable`. Changing this forces a new deployment to be created.
- `commit_message` (String) Git commit message associated with this deployment. Changing this forces a new deployment to be created.
- `commit_sha` (String) Git commit SHA associated with this deployment. Changing this forces a new deployment to be created.
- `debug` (Boolean) Enable debug mode for this deployment. Changing this forces a new deployment to be created.
diff --git a/internal/client/client.go b/internal/client/client.go
index 4028775..ea2dfc6 100644
--- a/internal/client/client.go
+++ b/internal/client/client.go
@@ -168,41 +168,48 @@ type App struct {
// AppConfig represents the configuration of an app.
type AppConfig struct {
- Spicepod interface{} `json:"spicepod,omitempty"`
- Registry string `json:"registry,omitempty"`
- Image string `json:"image,omitempty"`
- ImageTag string `json:"image_tag,omitempty"`
- UpdateChannel string `json:"update_channel,omitempty"`
- Replicas int `json:"replicas,omitempty"`
- Region string `json:"region,omitempty"`
- NodeGroup string `json:"node_group,omitempty"`
- StorageClaimSizeGB float64 `json:"storage_claim_size_gb,omitempty"`
+ Spicepod interface{} `json:"spicepod,omitempty"`
+ Registry string `json:"registry,omitempty"`
+ Image string `json:"image,omitempty"`
+ ImageTag string `json:"image_tag,omitempty"`
+ UpdateChannel string `json:"update_channel,omitempty"`
+ Replicas int `json:"replicas,omitempty"`
+ Resources *ContainerResources `json:"resources,omitempty"`
+ Executor *ExecutorConfig `json:"executor,omitempty"`
+ Region string `json:"region,omitempty"`
+ NodeGroup string `json:"node_group,omitempty"`
+ StorageClaimSizeGB float64 `json:"storage_claim_size_gb,omitempty"`
}
// CreateAppRequest represents the request to create an app.
type CreateAppRequest struct {
- Name string `json:"name"`
- Cname string `json:"cname"`
- Description string `json:"description,omitempty"`
- Visibility string `json:"visibility,omitempty"`
- Tags map[string]string `json:"tags,omitempty"`
+ Name string `json:"name"`
+ Cname string `json:"cname"`
+ Description string `json:"description,omitempty"`
+ Visibility string `json:"visibility,omitempty"`
+ Tags map[string]string `json:"tags,omitempty"`
+ Replicas *int `json:"replicas,omitempty"`
+ Resources *ContainerResources `json:"resources,omitempty"`
+ Executor *ExecutorConfig `json:"executor,omitempty"`
}
// UpdateAppRequest represents the request to update an app.
type UpdateAppRequest struct {
- Description string `json:"description,omitempty"`
- Visibility string `json:"visibility,omitempty"`
- ProductionBranch string `json:"production_branch,omitempty"`
- Tags map[string]string `json:"tags,omitempty"`
- Spicepod interface{} `json:"spicepod,omitempty"`
- Registry string `json:"registry,omitempty"`
- Image string `json:"image,omitempty"`
- ImageTag string `json:"image_tag,omitempty"`
- UpdateChannel string `json:"update_channel,omitempty"`
- Replicas *int `json:"replicas,omitempty"`
- NodeGroup string `json:"node_group,omitempty"`
- Region string `json:"region,omitempty"`
- StorageClaimSizeGB *float64 `json:"storage_claim_size_gb,omitempty"`
+ Description string `json:"description,omitempty"`
+ Visibility string `json:"visibility,omitempty"`
+ ProductionBranch string `json:"production_branch,omitempty"`
+ Tags map[string]string `json:"tags,omitempty"`
+ Spicepod interface{} `json:"spicepod,omitempty"`
+ Registry string `json:"registry,omitempty"`
+ Image string `json:"image,omitempty"`
+ ImageTag string `json:"image_tag,omitempty"`
+ UpdateChannel string `json:"update_channel,omitempty"`
+ Replicas *int `json:"replicas,omitempty"`
+ Resources *ContainerResources `json:"resources,omitempty"`
+ Executor *ExecutorConfig `json:"executor,omitempty"`
+ NodeGroup string `json:"node_group,omitempty"`
+ Region string `json:"region,omitempty"`
+ StorageClaimSizeGB *float64 `json:"storage_claim_size_gb,omitempty"`
}
// Deployment represents a Spice.ai deployment.
@@ -223,9 +230,35 @@ type Deployment struct {
CreatedBy int64 `json:"created_by,omitempty"`
}
+// ResourceRequests represents requested container resources.
+type ResourceRequests struct {
+ CPU string `json:"cpu,omitempty"`
+ Memory string `json:"memory,omitempty"`
+}
+
+// ResourceLimits represents container resource limits.
+type ResourceLimits struct {
+ CPU string `json:"cpu,omitempty"`
+ Memory string `json:"memory,omitempty"`
+ EphemeralStorage string `json:"ephemeral-storage,omitempty"`
+}
+
+// ContainerResources represents resource requests and limits for a container.
+type ContainerResources struct {
+ Limits *ResourceLimits `json:"limits,omitempty"`
+ Requests *ResourceRequests `json:"requests,omitempty"`
+}
+
+// ExecutorConfig represents executor container configuration.
+type ExecutorConfig struct {
+ Replicas *int `json:"replicas,omitempty"`
+ Resources *ContainerResources `json:"resources,omitempty"`
+}
+
// CreateDeploymentRequest represents the request to create a deployment.
type CreateDeploymentRequest struct {
ImageTag string `json:"image_tag,omitempty"`
+ Channel string `json:"channel,omitempty"`
Replicas *int `json:"replicas,omitempty"`
Branch string `json:"branch,omitempty"`
CommitSHA string `json:"commit_sha,omitempty"`
diff --git a/internal/provider/datasource_app.go b/internal/provider/datasource_app.go
index b354bc1..1418908 100644
--- a/internal/provider/datasource_app.go
+++ b/internal/provider/datasource_app.go
@@ -59,7 +59,6 @@ type AppDataSourceModel struct {
// Read-only attributes
CreatedAt types.String `tfsdk:"created_at"`
ClusterID types.String `tfsdk:"cluster_id"`
- APIKey types.String `tfsdk:"api_key"`
}
func (d *AppDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
@@ -153,11 +152,6 @@ func (d *AppDataSource) Schema(ctx context.Context, req datasource.SchemaRequest
MarkdownDescription: "The Kubernetes cluster identifier where the app is deployed.",
Computed: true,
},
- "api_key": schema.StringAttribute{
- MarkdownDescription: "The API key for the app.",
- Computed: true,
- Sensitive: true,
- },
},
}
}
@@ -236,7 +230,6 @@ func (d *AppDataSource) mapAppToModel(data *AppDataSourceModel, app *client.App)
data.CreatedAt = types.StringValue(app.CreatedAt)
data.ClusterID = types.StringValue(app.ClusterID)
- data.APIKey = types.StringValue(app.APIKey)
// Map cname
if app.Cname != "" {
diff --git a/internal/provider/datasource_apps.go b/internal/provider/datasource_apps.go
index 77ab7b7..500c019 100644
--- a/internal/provider/datasource_apps.go
+++ b/internal/provider/datasource_apps.go
@@ -64,7 +64,6 @@ type AppModel struct {
// Read-only attributes
CreatedAt types.String `tfsdk:"created_at"`
ClusterID types.String `tfsdk:"cluster_id"`
- APIKey types.String `tfsdk:"api_key"`
}
func (d *AppsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
@@ -163,11 +162,6 @@ func (d *AppsDataSource) Schema(ctx context.Context, req datasource.SchemaReques
MarkdownDescription: "The Kubernetes cluster identifier where the app is deployed.",
Computed: true,
},
- "api_key": schema.StringAttribute{
- MarkdownDescription: "The API key for the app.",
- Computed: true,
- Sensitive: true,
- },
},
},
},
@@ -229,7 +223,6 @@ func (d *AppsDataSource) mapAppToModel(app *client.App) AppModel {
ProductionBranch: types.StringValue(app.ProductionBranch),
CreatedAt: types.StringValue(app.CreatedAt),
ClusterID: types.StringValue(app.ClusterID),
- APIKey: types.StringValue(app.APIKey),
}
// Map tags
diff --git a/internal/provider/resource_app.go b/internal/provider/resource_app.go
index 5bb85fd..63be216 100644
--- a/internal/provider/resource_app.go
+++ b/internal/provider/resource_app.go
@@ -178,6 +178,8 @@ type AppResourceModel struct {
ImageTag types.String `tfsdk:"image_tag"`
UpdateChannel types.String `tfsdk:"update_channel"`
Replicas types.Int64 `tfsdk:"replicas"`
+ Resources types.Object `tfsdk:"resources"`
+ Executor types.Object `tfsdk:"executor"`
NodeGroup types.String `tfsdk:"node_group"`
Region types.String `tfsdk:"region"`
StorageClaimSizeGB types.Float64 `tfsdk:"storage_claim_size_gb"`
@@ -185,7 +187,6 @@ type AppResourceModel struct {
// Read-only attributes
CreatedAt types.String `tfsdk:"created_at"`
ClusterID types.String `tfsdk:"cluster_id"`
- APIKey types.String `tfsdk:"api_key"`
}
func (r *AppResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
@@ -305,11 +306,11 @@ resource "spiceai_app" "example" {
Computed: true,
},
"update_channel": schema.StringAttribute{
- MarkdownDescription: "Update channel for the spicepod. Valid values are `stable`, `nightly`, `internal`, `internal-sandbox`.",
+ MarkdownDescription: "Update channel for the spicepod. Valid values are `stable`, `preview`, `nightly`, and `internal`.",
Optional: true,
Computed: true,
Validators: []validator.String{
- stringvalidator.OneOf("stable", "nightly", "internal", "internal-sandbox"),
+ stringvalidator.OneOf("stable", "preview", "nightly", "internal"),
},
},
"replicas": schema.Int64Attribute{
@@ -320,6 +321,104 @@ resource "spiceai_app" "example" {
int64validator.Between(0, 10),
},
},
+ "resources": schema.SingleNestedAttribute{
+ MarkdownDescription: "Resource requests and limits for the app container.",
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "limits": schema.SingleNestedAttribute{
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "cpu": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ MarkdownDescription: "Whole-number vCPU limit, or `-` for no CPU limit.",
+ },
+ "memory": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ MarkdownDescription: "Memory limit in Gi (for example, `16Gi`).",
+ },
+ "ephemeral_storage": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ MarkdownDescription: "Ephemeral storage limit in Gi (for example, `8Gi`).",
+ },
+ },
+ },
+ "requests": schema.SingleNestedAttribute{
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "cpu": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ },
+ "memory": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ "executor": schema.SingleNestedAttribute{
+ MarkdownDescription: "Executor container configuration.",
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "replicas": schema.Int64Attribute{
+ Optional: true,
+ Computed: true,
+ MarkdownDescription: "Number of executor replicas.",
+ Validators: []validator.Int64{
+ int64validator.Between(0, 10),
+ },
+ },
+ "resources": schema.SingleNestedAttribute{
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "limits": schema.SingleNestedAttribute{
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "cpu": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ MarkdownDescription: "Whole-number vCPU limit, or `-` for no CPU limit.",
+ },
+ "memory": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ MarkdownDescription: "Memory limit in Gi (for example, `16Gi`).",
+ },
+ "ephemeral_storage": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ MarkdownDescription: "Ephemeral storage limit in Gi (for example, `8Gi`).",
+ },
+ },
+ },
+ "requests": schema.SingleNestedAttribute{
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "cpu": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ },
+ "memory": schema.StringAttribute{
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
"node_group": schema.StringAttribute{
MarkdownDescription: "The node group for the app deployment.",
Optional: true,
@@ -351,14 +450,6 @@ resource "spiceai_app" "example" {
stringplanmodifier.UseStateForUnknown(),
},
},
- "api_key": schema.StringAttribute{
- Computed: true,
- Sensitive: true,
- MarkdownDescription: "The API key for the app. This is used to authenticate requests to the app's endpoints.",
- PlanModifiers: []planmodifier.String{
- stringplanmodifier.UseStateForUnknown(),
- },
- },
},
}
}
@@ -539,6 +630,8 @@ func (r *AppResource) hasConfigAttributes(data *AppResourceModel) bool {
!data.ImageTag.IsNull() ||
!data.UpdateChannel.IsNull() ||
!data.Replicas.IsNull() ||
+ !data.Resources.IsNull() ||
+ !data.Executor.IsNull() ||
!data.NodeGroup.IsNull() ||
!data.Region.IsNull() ||
!data.StorageClaimSizeGB.IsNull() ||
@@ -602,6 +695,14 @@ func (r *AppResource) buildUpdateRequest(data *AppResourceModel) *client.UpdateA
updateReq.Replicas = &replicas
}
+ if !data.Resources.IsNull() && !data.Resources.IsUnknown() {
+ updateReq.Resources = expandContainerResources(data.Resources)
+ }
+
+ if !data.Executor.IsNull() && !data.Executor.IsUnknown() {
+ updateReq.Executor = expandExecutorConfig(data.Executor)
+ }
+
if !data.NodeGroup.IsNull() && !data.NodeGroup.IsUnknown() {
updateReq.NodeGroup = data.NodeGroup.ValueString()
}
@@ -694,12 +795,6 @@ func (r *AppResource) mapAppToModel(data *AppResourceModel, app *client.App, pre
data.CreatedAt = types.StringNull()
}
- if app.APIKey != "" {
- data.APIKey = types.StringValue(app.APIKey)
- } else {
- data.APIKey = types.StringNull()
- }
-
// Map config fields if available
if app.Config != nil {
if app.Config.Registry != "" {
@@ -728,6 +823,9 @@ func (r *AppResource) mapAppToModel(data *AppResourceModel, app *client.App, pre
data.Replicas = types.Int64Value(int64(app.Config.Replicas))
+ data.Resources = flattenContainerResources(app.Config.Resources)
+ data.Executor = flattenExecutorConfig(app.Config.Executor)
+
if app.Config.NodeGroup != "" {
data.NodeGroup = types.StringValue(app.Config.NodeGroup)
} else {
@@ -769,8 +867,245 @@ func (r *AppResource) mapAppToModel(data *AppResourceModel, app *client.App, pre
data.ImageTag = types.StringNull()
data.UpdateChannel = types.StringNull()
data.Replicas = types.Int64Null()
+ data.Resources = types.ObjectNull(map[string]attr.Type{
+ "limits": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ "ephemeral_storage": types.StringType,
+ }},
+ "requests": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ }},
+ })
+ data.Executor = types.ObjectNull(map[string]attr.Type{
+ "replicas": types.Int64Type,
+ "resources": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "limits": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ "ephemeral_storage": types.StringType,
+ }},
+ "requests": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ }},
+ }},
+ })
data.NodeGroup = types.StringNull()
data.StorageClaimSizeGB = types.Float64Null()
data.Spicepod = SpicepodStringValue{StringValue: types.StringNull()}
}
}
+
+func expandContainerResources(value types.Object) *client.ContainerResources {
+ if value.IsNull() || value.IsUnknown() {
+ return nil
+ }
+
+ result := &client.ContainerResources{}
+ hasValues := false
+
+ attrs := value.Attributes()
+
+ if limitsAttr, ok := attrs["limits"]; ok {
+ if limitsObj, ok := limitsAttr.(types.Object); ok && !limitsObj.IsNull() && !limitsObj.IsUnknown() {
+ limits := &client.ResourceLimits{}
+ limitsHasValues := false
+
+ for name, attrValue := range limitsObj.Attributes() {
+ strValue, ok := attrValue.(types.String)
+ if !ok || strValue.IsNull() || strValue.IsUnknown() {
+ continue
+ }
+
+ switch name {
+ case "cpu":
+ limits.CPU = strValue.ValueString()
+ limitsHasValues = true
+ case "memory":
+ limits.Memory = strValue.ValueString()
+ limitsHasValues = true
+ case "ephemeral_storage":
+ limits.EphemeralStorage = strValue.ValueString()
+ limitsHasValues = true
+ }
+ }
+
+ if limitsHasValues {
+ result.Limits = limits
+ hasValues = true
+ }
+ }
+ }
+
+ if requestsAttr, ok := attrs["requests"]; ok {
+ if requestsObj, ok := requestsAttr.(types.Object); ok && !requestsObj.IsNull() && !requestsObj.IsUnknown() {
+ requests := &client.ResourceRequests{}
+ requestsHasValues := false
+
+ for name, attrValue := range requestsObj.Attributes() {
+ strValue, ok := attrValue.(types.String)
+ if !ok || strValue.IsNull() || strValue.IsUnknown() {
+ continue
+ }
+
+ switch name {
+ case "cpu":
+ requests.CPU = strValue.ValueString()
+ requestsHasValues = true
+ case "memory":
+ requests.Memory = strValue.ValueString()
+ requestsHasValues = true
+ }
+ }
+
+ if requestsHasValues {
+ result.Requests = requests
+ hasValues = true
+ }
+ }
+ }
+
+ if !hasValues {
+ return nil
+ }
+
+ return result
+}
+
+func expandExecutorConfig(value types.Object) *client.ExecutorConfig {
+ if value.IsNull() || value.IsUnknown() {
+ return nil
+ }
+
+ result := &client.ExecutorConfig{}
+ hasValues := false
+
+ attrs := value.Attributes()
+
+ if replicasAttr, ok := attrs["replicas"]; ok {
+ if replicasValue, ok := replicasAttr.(types.Int64); ok && !replicasValue.IsNull() && !replicasValue.IsUnknown() {
+ replicas := int(replicasValue.ValueInt64())
+ result.Replicas = &replicas
+ hasValues = true
+ }
+ }
+
+ if resourcesAttr, ok := attrs["resources"]; ok {
+ if resourcesObj, ok := resourcesAttr.(types.Object); ok && !resourcesObj.IsNull() && !resourcesObj.IsUnknown() {
+ result.Resources = expandContainerResources(resourcesObj)
+ if result.Resources != nil {
+ hasValues = true
+ }
+ }
+ }
+
+ if !hasValues {
+ return nil
+ }
+
+ return result
+}
+
+func flattenContainerResources(resources *client.ContainerResources) types.Object {
+ containerResourcesType := map[string]attr.Type{
+ "limits": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ "ephemeral_storage": types.StringType,
+ }},
+ "requests": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ }},
+ }
+
+ if resources == nil {
+ return types.ObjectNull(containerResourcesType)
+ }
+
+ limitsType := map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ "ephemeral_storage": types.StringType,
+ }
+
+ requestsType := map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ }
+
+ limitsValue := types.ObjectNull(limitsType)
+ if resources.Limits != nil {
+ limitsValue = types.ObjectValueMust(
+ limitsType,
+ map[string]attr.Value{
+ "cpu": stringValueOrNull(resources.Limits.CPU),
+ "memory": stringValueOrNull(resources.Limits.Memory),
+ "ephemeral_storage": stringValueOrNull(resources.Limits.EphemeralStorage),
+ },
+ )
+ }
+
+ requestsValue := types.ObjectNull(requestsType)
+ if resources.Requests != nil {
+ requestsValue = types.ObjectValueMust(
+ requestsType,
+ map[string]attr.Value{
+ "cpu": stringValueOrNull(resources.Requests.CPU),
+ "memory": stringValueOrNull(resources.Requests.Memory),
+ },
+ )
+ }
+
+ return types.ObjectValueMust(
+ containerResourcesType,
+ map[string]attr.Value{
+ "limits": limitsValue,
+ "requests": requestsValue,
+ },
+ )
+}
+
+func flattenExecutorConfig(executor *client.ExecutorConfig) types.Object {
+ executorType := map[string]attr.Type{
+ "replicas": types.Int64Type,
+ "resources": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "limits": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ "ephemeral_storage": types.StringType,
+ }},
+ "requests": types.ObjectType{AttrTypes: map[string]attr.Type{
+ "cpu": types.StringType,
+ "memory": types.StringType,
+ }},
+ }},
+ }
+
+ if executor == nil {
+ return types.ObjectNull(executorType)
+ }
+
+ replicasValue := types.Int64Null()
+ if executor.Replicas != nil {
+ replicasValue = types.Int64Value(int64(*executor.Replicas))
+ }
+
+ return types.ObjectValueMust(
+ executorType,
+ map[string]attr.Value{
+ "replicas": replicasValue,
+ "resources": flattenContainerResources(executor.Resources),
+ },
+ )
+}
+
+func stringValueOrNull(value string) attr.Value {
+ if value == "" {
+ return types.StringNull()
+ }
+
+ return types.StringValue(value)
+}
diff --git a/internal/provider/resource_deployment.go b/internal/provider/resource_deployment.go
index fd5511e..f6462e6 100644
--- a/internal/provider/resource_deployment.go
+++ b/internal/provider/resource_deployment.go
@@ -42,6 +42,7 @@ type DeploymentResourceModel struct {
AppID types.String `tfsdk:"app_id"`
Triggers types.Map `tfsdk:"triggers"`
ImageTag types.String `tfsdk:"image_tag"`
+ Channel types.String `tfsdk:"channel"`
Replicas types.Int64 `tfsdk:"replicas"`
Branch types.String `tfsdk:"branch"`
CommitSHA types.String `tfsdk:"commit_sha"`
@@ -138,6 +139,13 @@ resource "spiceai_deployment" "example" {
stringplanmodifier.UseStateForUnknown(),
},
},
+ "channel": schema.StringAttribute{
+ MarkdownDescription: "Override the deployment channel for this deployment. Valid values are `stable`. Changing this forces a new deployment to be created.",
+ Optional: true,
+ PlanModifiers: []planmodifier.String{
+ stringplanmodifier.RequiresReplace(),
+ },
+ },
"replicas": schema.Int64Attribute{
MarkdownDescription: "Override the number of replicas for this deployment. Must be between 0 and 10. If not specified, uses the app's configured replicas. Changing this forces a new deployment to be created.",
Optional: true,
@@ -262,6 +270,10 @@ func (r *DeploymentResource) Create(ctx context.Context, req resource.CreateRequ
createReq.ImageTag = data.ImageTag.ValueString()
}
+ if !data.Channel.IsNull() && !data.Channel.IsUnknown() {
+ createReq.Channel = data.Channel.ValueString()
+ }
+
if !data.Replicas.IsNull() && !data.Replicas.IsUnknown() {
replicas := int(data.Replicas.ValueInt64())
createReq.Replicas = &replicas