From 8a94154e4bf4e67edc974f92d156c7cd8b260dc3 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Wed, 5 Nov 2025 22:51:38 +0100 Subject: [PATCH 01/26] merge --- .../basic/databricks.yml.tmpl | 7 + .../basic/out.requests.txt | 88 ++++++++ .../basic/out.test.toml | 5 + .../model_serving_endpoints/basic/output.txt | 34 +++ .../model_serving_endpoints/basic/script | 15 ++ .../model_serving_endpoints/basic/test.toml | 2 + .../update/databricks.yml.tmpl | 14 ++ .../update/out.test.toml | 5 + .../model_serving_endpoints/update/output.txt | 113 ++++++++++ .../model_serving_endpoints/update/script | 37 +++ .../model_serving_endpoints/update/test.toml | 2 + bundle/direct/dresources/all.go | 46 ++-- bundle/direct/dresources/all_test.go | 52 +++++ .../dresources/model_serving_endpoint.go | 144 ++++++++++++ libs/testserver/fake_workspace.go | 3 + libs/testserver/handlers.go | 17 ++ libs/testserver/serving_endpoints.go | 212 ++++++++++++++++++ 17 files changed, 774 insertions(+), 22 deletions(-) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/basic/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/output.txt create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/test.toml create mode 100644 bundle/direct/dresources/model_serving_endpoint.go create mode 100644 libs/testserver/serving_endpoints.go diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl new file mode 100644 index 0000000000..97ce316da7 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl @@ -0,0 +1,7 @@ +bundle: + name: acc-$UNIQUE_NAME + +resources: + model_serving_endpoints: + my_endpoint: + name: test-endpoint-$UNIQUE_NAME diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt new file mode 100644 index 0000000000..0041483871 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt @@ -0,0 +1,88 @@ +{ + "method": "GET", + "path": "/api/2.0/preview/scim/v2/Me" +} +{ + "method": "GET", + "path": "/api/2.0/preview/scim/v2/Me" +} +{ + "method": "GET", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" +} +{ + "method": "GET", + "path": "/api/2.0/preview/scim/v2/Me" +} +{ + "method": "GET", + "path": "/api/2.0/workspace/get-status", + "q": { + "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default" + } +} +{ + "method": "POST", + "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock", + "q": { + "overwrite": "false" + }, + "body": { + "ID": "[UUID]", + "AcquisitionTime": "[TIMESTAMP]", + "IsForced": false, + "User": "[USERNAME]" + } +} +{ + "method": "GET", + "path": "/api/2.0/workspace/get-status", + "q": { + "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock", + "return_export_info": "true" + } +} +{ + "method": "GET", + "path": "/api/2.0/workspace/export", + "q": { + "direct_download": "true", + "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock" + } +} +{ + "method": "GET", + "path": "/api/2.0/workspace/get-status", + "q": { + "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/terraform.tfstate", + "return_export_info": "true" + } +} +{ + "method": "GET", + "path": "/api/2.0/workspace-files/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/terraform.tfstate" +} +{ + "method": "GET", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" +} +{ + "method": "POST", + "path": "/api/2.0/workspace/delete", + "body": { + "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default", + "recursive": true + } +} +{ + "method": "GET", + "path": "/api/2.0/workspace/get-status", + "q": { + "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock", + "return_export_info": "true" + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml new file mode 100644 index 0000000000..43c8f792f5 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = true + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt new file mode 100644 index 0000000000..cf636e82f8 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt @@ -0,0 +1,34 @@ + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "name": "[ENDPOINT_ID]" + } +} + +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +{ + "creator":"[USERNAME]", + "id":"[UUID]", + "name":"[ENDPOINT_ID]", + "state": { + "config_update":"NOT_UPDATING" + } +} + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint my_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/script b/acceptance/bundle/resources/model_serving_endpoints/basic/script new file mode 100755 index 0000000000..0f208977ae --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/script @@ -0,0 +1,15 @@ +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve +} +trap cleanup EXIT + +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +endpoint_name=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.my_endpoint.name') +echo "$endpoint_name:ENDPOINT_ID" >> ACC_REPLS + +trace $CLI serving-endpoints get $endpoint_name diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml new file mode 100644 index 0000000000..c72905b0fe --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml @@ -0,0 +1,2 @@ +Timeout = '5m' +Cloud = true diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/update/databricks.yml.tmpl new file mode 100644 index 0000000000..e5e4a7c31d --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/databricks.yml.tmpl @@ -0,0 +1,14 @@ +bundle: + name: acc-$UNIQUE_NAME + +resources: + model_serving_endpoints: + my_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: test-entity + entity_name: my-model + entity_version: "1" + workload_size: Small + scale_to_zero_enabled: true diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/out.test.toml new file mode 100644 index 0000000000..43c8f792f5 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = true + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/output.txt new file mode 100644 index 0000000000..ef5069306d --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/output.txt @@ -0,0 +1,113 @@ + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +=== Initial deployment +>>> print_requests +{ + "body": { + "config": { + "served_entities": [ + { + "entity_name": "my-model", + "entity_version": "1", + "name": "test-entity", + "scale_to_zero_enabled": true, + "workload_size": "Small" + } + ] + }, + "name": "[ENDPOINT_ID]" + }, + "method": "POST", + "path": "/api/2.0/serving-endpoints" +} + +=== Update the endpoint configuration (change model version) +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests +{ + "body": { + "served_entities": [ + { + "entity_name": "my-model", + "entity_version": "2", + "name": "test-entity", + "scale_to_zero_enabled": true, + "workload_size": "Small" + } + ] + }, + "method": "PUT", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config" +} + +=== Verify the endpoint was updated (not recreated) +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +{ + "config": { + "served_entities": [ + { + "entity_name":"my-model", + "entity_version":"2", + "name":"test-entity", + "scale_to_zero_enabled":true, + "workload_size":"Small" + } + ] + }, + "creator":"[USERNAME]", + "id":"[UUID]", + "name":"[ENDPOINT_ID]", + "state": { + "config_update":"NOT_UPDATING" + } +} + +=== Verify endpoint is in NOT_UPDATING state after update +Endpoint is in NOT_UPDATING state + +=== Verify the model version was updated +Model version updated to 2 + +=== Destroy the endpoint +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint my_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default + +Deleting files... +Destroy complete! + +>>> print_requests +{ + "body": { + "served_entities": [ + { + "entity_name": "my-model", + "entity_version": "2", + "name": "test-entity", + "scale_to_zero_enabled": true, + "workload_size": "Small" + } + ] + }, + "method": "PUT", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config" +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" +} + +>>> musterr [CLI] serving-endpoints get [ENDPOINT_ID] +Error: Serving endpoint with name [ENDPOINT_ID] not found diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/script b/acceptance/bundle/resources/model_serving_endpoints/update/script new file mode 100644 index 0000000000..3cb25bc932 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/script @@ -0,0 +1,37 @@ +envsubst < databricks.yml.tmpl > databricks.yml +trace $CLI bundle deploy + +print_requests() { + jq --sort-keys 'select(.method != "GET" and (.path | contains("/serving-endpoints")))' < out.requests.txt +} + +title "Initial deployment" +trace print_requests + +endpoint_name=$(jq -r 'select(.method == "POST" and (.path | contains("/serving-endpoints"))) | .body.name' < out.requests.txt | head -1) +echo "$endpoint_name:ENDPOINT_ID" >> ACC_REPLS +rm out.requests.txt + +title "Update the endpoint configuration (change model version)" +update_file.py databricks.yml "1" "2" +trace $CLI bundle deploy +trace print_requests + +title "Verify the endpoint was updated (not recreated)" +trace $CLI serving-endpoints get $endpoint_name + +title "Verify endpoint is in NOT_UPDATING state after update" +$CLI serving-endpoints get $endpoint_name | jq -r '.state.config_update' | grep -q 'NOT_UPDATING' +echo "" +echo "Endpoint is in NOT_UPDATING state" + +title "Verify the model version was updated" +$CLI serving-endpoints get $endpoint_name | jq -r '.config.served_entities[0].entity_version' | grep -q '2' +echo "" +echo "Model version updated to 2" + +title "Destroy the endpoint" +trace $CLI bundle destroy --auto-approve +trace print_requests +trace musterr $CLI serving-endpoints get $endpoint_name +rm out.requests.txt diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/test.toml new file mode 100644 index 0000000000..c72905b0fe --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/test.toml @@ -0,0 +1,2 @@ +Timeout = '5m' +Cloud = true diff --git a/bundle/direct/dresources/all.go b/bundle/direct/dresources/all.go index 3d2d3a4d46..8a3342428e 100644 --- a/bundle/direct/dresources/all.go +++ b/bundle/direct/dresources/all.go @@ -7,30 +7,32 @@ import ( ) var SupportedResources = map[string]any{ - "jobs": (*ResourceJob)(nil), - "pipelines": (*ResourcePipeline)(nil), - "experiments": (*ResourceExperiment)(nil), - "schemas": (*ResourceSchema)(nil), - "volumes": (*ResourceVolume)(nil), - "models": (*ResourceMlflowModel)(nil), - "apps": (*ResourceApp)(nil), - "sql_warehouses": (*ResourceSqlWarehouse)(nil), - "database_instances": (*ResourceDatabaseInstance)(nil), - "database_catalogs": (*ResourceDatabaseCatalog)(nil), - "synced_database_tables": (*ResourceSyncedDatabaseTable)(nil), - "alerts": (*ResourceAlert)(nil), - "clusters": (*ResourceCluster)(nil), - "registered_models": (*ResourceRegisteredModel)(nil), + "jobs": (*ResourceJob)(nil), + "pipelines": (*ResourcePipeline)(nil), + "experiments": (*ResourceExperiment)(nil), + "schemas": (*ResourceSchema)(nil), + "volumes": (*ResourceVolume)(nil), + "models": (*ResourceMlflowModel)(nil), + "apps": (*ResourceApp)(nil), + "sql_warehouses": (*ResourceSqlWarehouse)(nil), + "database_instances": (*ResourceDatabaseInstance)(nil), + "database_catalogs": (*ResourceDatabaseCatalog)(nil), + "synced_database_tables": (*ResourceSyncedDatabaseTable)(nil), + "alerts": (*ResourceAlert)(nil), + "clusters": (*ResourceCluster)(nil), + "registered_models": (*ResourceRegisteredModel)(nil), + "model_serving_endpoints": (*ResourceModelServingEndpoint)(nil), // Permissions - "jobs.permissions": (*ResourcePermissions)(nil), - "pipelines.permissions": (*ResourcePermissions)(nil), - "apps.permissions": (*ResourcePermissions)(nil), - "clusters.permissions": (*ResourcePermissions)(nil), - "database_instances.permissions": (*ResourcePermissions)(nil), - "experiments.permissions": (*ResourcePermissions)(nil), - "models.permissions": (*ResourcePermissions)(nil), - "sql_warehouses.permissions": (*ResourcePermissions)(nil), + "jobs.permissions": (*ResourcePermissions)(nil), + "pipelines.permissions": (*ResourcePermissions)(nil), + "apps.permissions": (*ResourcePermissions)(nil), + "clusters.permissions": (*ResourcePermissions)(nil), + "database_instances.permissions": (*ResourcePermissions)(nil), + "experiments.permissions": (*ResourcePermissions)(nil), + "models.permissions": (*ResourcePermissions)(nil), + "sql_warehouses.permissions": (*ResourcePermissions)(nil), + "model_serving_endpoints.permissions": (*ResourcePermissions)(nil), } func InitAll(client *databricks.WorkspaceClient) (map[string]*Adapter, error) { diff --git a/bundle/direct/dresources/all_test.go b/bundle/direct/dresources/all_test.go index d18b408a4c..df4723ad54 100644 --- a/bundle/direct/dresources/all_test.go +++ b/bundle/direct/dresources/all_test.go @@ -24,6 +24,7 @@ import ( "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/ml" "github.com/databricks/databricks-sdk-go/service/pipelines" + "github.com/databricks/databricks-sdk-go/service/serving" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -97,6 +98,30 @@ var testConfig map[string]any = map[string]any{ }, }, }, + + "model_serving_endpoints": &resources.ModelServingEndpoint{ + CreateServingEndpoint: serving.CreateServingEndpoint{ + Name: "my-endpoint", + Config: &serving.EndpointCoreConfigInput{ + ServedModels: []serving.ServedModelInput{ + { + ModelName: "model-name", + ModelVersion: "1", + WorkloadSize: "Small", + ScaleToZeroEnabled: true, + }, + }, + TrafficConfig: &serving.TrafficConfig{ + Routes: []serving.Route{ + { + ServedModelName: "model-name-1", + TrafficPercentage: 100, + }, + }, + }, + }, + }, + }, } type prepareWorkspace func(client *databricks.WorkspaceClient) (any, error) @@ -252,6 +277,33 @@ var testDeps = map[string]prepareWorkspace{ }}, }, nil }, + + "model_serving_endpoints.permissions": func(client *databricks.WorkspaceClient) (any, error) { + waiter, err := client.ServingEndpoints.Create(context.Background(), serving.CreateServingEndpoint{ + Name: "endpoint-permissions", + Config: &serving.EndpointCoreConfigInput{ + ServedModels: []serving.ServedModelInput{ + { + ModelName: "model-name", + ModelVersion: "1", + WorkloadSize: "Small", + ScaleToZeroEnabled: true, + }, + }, + }, + }) + if err != nil { + return nil, err + } + + return &PermissionsState{ + ObjectID: "/serving-endpoints/" + waiter.Response.Name, + Permissions: []iam.AccessControlRequest{{ + PermissionLevel: "CAN_MANAGE", + UserName: "user@example.com", + }}, + }, nil + }, } func TestAll(t *testing.T) { diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go new file mode 100644 index 0000000000..18b172a60e --- /dev/null +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -0,0 +1,144 @@ +package dresources + +import ( + "context" + "time" + + "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/databricks-sdk-go" + "github.com/databricks/databricks-sdk-go/service/serving" +) + +type ResourceModelServingEndpoint struct { + client *databricks.WorkspaceClient +} + +func (*ResourceModelServingEndpoint) New(client *databricks.WorkspaceClient) *ResourceModelServingEndpoint { + return &ResourceModelServingEndpoint{ + client: client, + } +} + +func (*ResourceModelServingEndpoint) PrepareState(input *resources.ModelServingEndpoint) *serving.CreateServingEndpoint { + return &input.CreateServingEndpoint +} + +func autoCaptureConfigOutputToInput(output *serving.AutoCaptureConfigOutput) *serving.AutoCaptureConfigInput { + if output == nil { + return nil + } + return &serving.AutoCaptureConfigInput{ + CatalogName: output.CatalogName, + SchemaName: output.SchemaName, + TableNamePrefix: output.TableNamePrefix, + Enabled: output.Enabled, + ForceSendFields: filterFields[serving.AutoCaptureConfigInput](output.ForceSendFields), + } +} + +func servedEntitiesOutputToInput(output []serving.ServedEntityOutput) []serving.ServedEntityInput { + if len(output) == 0 { + return nil + } + entities := make([]serving.ServedEntityInput, len(output)) + for i, entity := range output { + entities[i] = serving.ServedEntityInput{ + EntityName: entity.EntityName, + EntityVersion: entity.EntityVersion, + EnvironmentVars: entity.EnvironmentVars, + ExternalModel: entity.ExternalModel, + InstanceProfileArn: entity.InstanceProfileArn, + MaxProvisionedConcurrency: entity.MaxProvisionedConcurrency, + MaxProvisionedThroughput: entity.MaxProvisionedThroughput, + MinProvisionedConcurrency: entity.MinProvisionedConcurrency, + MinProvisionedThroughput: entity.MinProvisionedThroughput, + Name: entity.Name, + ProvisionedModelUnits: entity.ProvisionedModelUnits, + ScaleToZeroEnabled: entity.ScaleToZeroEnabled, + WorkloadSize: entity.WorkloadSize, + WorkloadType: entity.WorkloadType, + ForceSendFields: filterFields[serving.ServedEntityInput](entity.ForceSendFields), + } + } + + return entities +} + +func configOutputToInput(output *serving.EndpointCoreConfigOutput) *serving.EndpointCoreConfigInput { + if output == nil { + return nil + } + return &serving.EndpointCoreConfigInput{ + AutoCaptureConfig: autoCaptureConfigOutputToInput(output.AutoCaptureConfig), + ServedEntities: servedEntitiesOutputToInput(output.ServedEntities), + } +} + +// TODO: Remap served_models to served_entities. +func (*ResourceModelServingEndpoint) RemapState(endpoint *serving.ServingEndpointDetailed) *serving.CreateServingEndpoint { + // Map the remote state (ServingEndpointDetailed) to the local state (CreateServingEndpoint) + // for proper comparison during diff calculation + return &serving.CreateServingEndpoint{ + AiGateway: endpoint.AiGateway, + BudgetPolicyId: endpoint.BudgetPolicyId, + Config: configOutputToInput(endpoint.Config), + Description: endpoint.Description, + EmailNotifications: endpoint.EmailNotifications, + Name: endpoint.Name, + RouteOptimized: endpoint.RouteOptimized, + Tags: endpoint.Tags, + ForceSendFields: filterFields[serving.CreateServingEndpoint](endpoint.ForceSendFields), + } +} + +func (r *ResourceModelServingEndpoint) DoRefresh(ctx context.Context, id string) (*serving.ServingEndpointDetailed, error) { + return r.client.ServingEndpoints.GetByName(ctx, id) +} + +func (r *ResourceModelServingEndpoint) DoCreate(ctx context.Context, config *serving.CreateServingEndpoint) (string, error) { + waiter, err := r.client.ServingEndpoints.Create(ctx, *config) + if err != nil { + return "", err + } + + return waiter.Response.Name, nil +} + +// waitForEndpointReady waits for the serving endpoint to be ready (not updating) +func (r *ResourceModelServingEndpoint) waitForEndpointReady(ctx context.Context, name string) (*serving.ServingEndpointDetailed, error) { + waiter := &serving.WaitGetServingEndpointNotUpdating[serving.ServingEndpointDetailed]{ + Response: &serving.ServingEndpointDetailed{Name: name}, + Name: name, + Poll: func(timeout time.Duration, callback func(*serving.ServingEndpointDetailed)) (*serving.ServingEndpointDetailed, error) { + return r.client.ServingEndpoints.WaitGetServingEndpointNotUpdating(ctx, name, timeout, callback) + }, + } + + // Model serving endpoints can take a long time to spin up. We match the timeout from TF here (35 minutes). + return waiter.GetWithTimeout(35 * time.Minute) +} + +func (r *ResourceModelServingEndpoint) WaitAfterCreate(ctx context.Context, config *serving.CreateServingEndpoint) (*serving.ServingEndpointDetailed, error) { + return r.waitForEndpointReady(ctx, config.Name) +} + +func (r *ResourceModelServingEndpoint) DoUpdate(ctx context.Context, id string, config *serving.CreateServingEndpoint) error { + if config.Config == nil { + return nil + } + + // UpdateConfig expects an EndpointCoreConfigInput with the Name field set + updateConfig := *config.Config + updateConfig.Name = id + + _, err := r.client.ServingEndpoints.UpdateConfig(ctx, updateConfig) + return err +} + +func (r *ResourceModelServingEndpoint) WaitAfterUpdate(ctx context.Context, config *serving.CreateServingEndpoint) (*serving.ServingEndpointDetailed, error) { + return r.waitForEndpointReady(ctx, config.Name) +} + +func (r *ResourceModelServingEndpoint) DoDelete(ctx context.Context, id string) error { + return r.client.ServingEndpoints.DeleteByName(ctx, id) +} diff --git a/libs/testserver/fake_workspace.go b/libs/testserver/fake_workspace.go index 3c8165d998..742c0f1e23 100644 --- a/libs/testserver/fake_workspace.go +++ b/libs/testserver/fake_workspace.go @@ -22,6 +22,7 @@ import ( "github.com/databricks/databricks-sdk-go/service/jobs" "github.com/databricks/databricks-sdk-go/service/ml" "github.com/databricks/databricks-sdk-go/service/pipelines" + "github.com/databricks/databricks-sdk-go/service/serving" "github.com/databricks/databricks-sdk-go/service/sql" "github.com/databricks/databricks-sdk-go/service/workspace" ) @@ -107,6 +108,7 @@ type FakeWorkspace struct { Clusters map[string]compute.ClusterDetails Catalogs map[string]catalog.CatalogInfo RegisteredModels map[string]catalog.RegisteredModelInfo + ServingEndpoints map[string]serving.ServingEndpointDetailed Acls map[string][]workspace.AclItem @@ -205,6 +207,7 @@ func NewFakeWorkspace(url, token string) *FakeWorkspace { Dashboards: map[string]dashboards.Dashboard{}, PublishedDashboards: map[string]dashboards.PublishedDashboard{}, SqlWarehouses: map[string]sql.GetWarehouseResponse{}, + ServingEndpoints: map[string]serving.ServingEndpointDetailed{}, Repos: map[string]workspace.RepoInfo{}, Acls: map[string][]workspace.AclItem{}, Permissions: map[string]iam.ObjectPermissions{}, diff --git a/libs/testserver/handlers.go b/libs/testserver/handlers.go index d826d743ee..f3c54d8aab 100644 --- a/libs/testserver/handlers.go +++ b/libs/testserver/handlers.go @@ -599,6 +599,23 @@ func AddDefaultHandlers(server *Server) { return MapDelete(req.Workspace, req.Workspace.ModelRegistryModels, req.URL.Query().Get("name")) }) + // Serving Endpoints: + server.Handle("GET", "/api/2.0/serving-endpoints/{name}", func(req Request) any { + return MapGet(req.Workspace, req.Workspace.ServingEndpoints, req.Vars["name"]) + }) + + server.Handle("POST", "/api/2.0/serving-endpoints", func(req Request) any { + return req.Workspace.ServingEndpointCreate(req) + }) + + server.Handle("PUT", "/api/2.0/serving-endpoints/{name}/config", func(req Request) any { + return req.Workspace.ServingEndpointUpdate(req, req.Vars["name"]) + }) + + server.Handle("DELETE", "/api/2.0/serving-endpoints/{name}", func(req Request) any { + return MapDelete(req.Workspace, req.Workspace.ServingEndpoints, req.Vars["name"]) + }) + // Generic permissions endpoints server.Handle("GET", "/api/2.0/permissions/{object_type}/{object_id}", func(req Request) any { return req.Workspace.GetPermissions(req) diff --git a/libs/testserver/serving_endpoints.go b/libs/testserver/serving_endpoints.go new file mode 100644 index 0000000000..61975a8730 --- /dev/null +++ b/libs/testserver/serving_endpoints.go @@ -0,0 +1,212 @@ +package testserver + +import ( + "encoding/json" + "fmt" + + "github.com/databricks/databricks-sdk-go/service/serving" +) + +func (s *FakeWorkspace) ServingEndpointCreate(req Request) Response { + defer s.LockUnlock()() + + var createReq serving.CreateServingEndpoint + err := json.Unmarshal(req.Body, &createReq) + if err != nil { + return Response{ + Body: fmt.Sprintf("cannot unmarshal request body: %s", err), + StatusCode: 400, + } + } + + // Check if endpoint with this name already exists + if _, exists := s.ServingEndpoints[createReq.Name]; exists { + return Response{ + StatusCode: 409, + Body: map[string]string{"error_code": "RESOURCE_ALREADY_EXISTS", "message": fmt.Sprintf("Serving endpoint with name %s already exists", createReq.Name)}, + } + } + + // Convert config to output format + var config *serving.EndpointCoreConfigOutput + if createReq.Config != nil { + config = &serving.EndpointCoreConfigOutput{ + TrafficConfig: createReq.Config.TrafficConfig, + } + + // Convert ServedEntityInput to ServedEntityOutput + if len(createReq.Config.ServedEntities) > 0 { + config.ServedEntities = make([]serving.ServedEntityOutput, len(createReq.Config.ServedEntities)) + for i, entity := range createReq.Config.ServedEntities { + config.ServedEntities[i] = serving.ServedEntityOutput{ + EntityName: entity.EntityName, + EntityVersion: entity.EntityVersion, + EnvironmentVars: entity.EnvironmentVars, + ExternalModel: entity.ExternalModel, + InstanceProfileArn: entity.InstanceProfileArn, + MaxProvisionedConcurrency: entity.MaxProvisionedConcurrency, + MaxProvisionedThroughput: entity.MaxProvisionedThroughput, + MinProvisionedConcurrency: entity.MinProvisionedConcurrency, + MinProvisionedThroughput: entity.MinProvisionedThroughput, + Name: entity.Name, + ProvisionedModelUnits: entity.ProvisionedModelUnits, + ScaleToZeroEnabled: entity.ScaleToZeroEnabled, + WorkloadSize: entity.WorkloadSize, + WorkloadType: entity.WorkloadType, + ForceSendFields: entity.ForceSendFields, + } + } + } + + // Convert ServedModelInput to ServedModelOutput + if len(createReq.Config.ServedModels) > 0 { + config.ServedModels = make([]serving.ServedModelOutput, len(createReq.Config.ServedModels)) + for i, model := range createReq.Config.ServedModels { + config.ServedModels[i] = serving.ServedModelOutput{ + EnvironmentVars: model.EnvironmentVars, + InstanceProfileArn: model.InstanceProfileArn, + MaxProvisionedConcurrency: model.MaxProvisionedConcurrency, + MinProvisionedConcurrency: model.MinProvisionedConcurrency, + ModelName: model.ModelName, + ModelVersion: model.ModelVersion, + Name: model.Name, + ProvisionedModelUnits: model.ProvisionedModelUnits, + ScaleToZeroEnabled: model.ScaleToZeroEnabled, + WorkloadSize: model.WorkloadSize, + WorkloadType: serving.ServingModelWorkloadType(model.WorkloadType), + ForceSendFields: model.ForceSendFields, + } + } + } + + // Convert AutoCaptureConfig if present + if createReq.Config.AutoCaptureConfig != nil { + config.AutoCaptureConfig = &serving.AutoCaptureConfigOutput{ + CatalogName: createReq.Config.AutoCaptureConfig.CatalogName, + SchemaName: createReq.Config.AutoCaptureConfig.SchemaName, + TableNamePrefix: createReq.Config.AutoCaptureConfig.TableNamePrefix, + Enabled: createReq.Config.AutoCaptureConfig.Enabled, + ForceSendFields: createReq.Config.AutoCaptureConfig.ForceSendFields, + } + } + } + + endpoint := serving.ServingEndpointDetailed{ + AiGateway: createReq.AiGateway, + BudgetPolicyId: createReq.BudgetPolicyId, + Config: config, + Creator: s.CurrentUser().UserName, + Description: createReq.Description, + EmailNotifications: createReq.EmailNotifications, + Id: nextUUID(), + Name: createReq.Name, + RouteOptimized: createReq.RouteOptimized, + Tags: createReq.Tags, + State: &serving.EndpointState{ + ConfigUpdate: serving.EndpointStateConfigUpdateNotUpdating, + }, + ForceSendFields: createReq.ForceSendFields, + } + + s.ServingEndpoints[createReq.Name] = endpoint + + return Response{ + Body: endpoint, + } +} + +func (s *FakeWorkspace) ServingEndpointUpdate(req Request, name string) Response { + defer s.LockUnlock()() + + var updateReq serving.EndpointCoreConfigInput + err := json.Unmarshal(req.Body, &updateReq) + if err != nil { + return Response{ + Body: fmt.Sprintf("cannot unmarshal request body: %s", err), + StatusCode: 400, + } + } + + endpoint, exists := s.ServingEndpoints[name] + if !exists { + return Response{ + StatusCode: 404, + Body: map[string]string{"error_code": "RESOURCE_DOES_NOT_EXIST", "message": fmt.Sprintf("Serving endpoint with name %s not found", name)}, + } + } + + // Convert config to output format + var config *serving.EndpointCoreConfigOutput + if updateReq.ServedEntities != nil || updateReq.ServedModels != nil || updateReq.TrafficConfig != nil { + config = &serving.EndpointCoreConfigOutput{ + TrafficConfig: updateReq.TrafficConfig, + } + + // Convert ServedEntityInput to ServedEntityOutput + if len(updateReq.ServedEntities) > 0 { + config.ServedEntities = make([]serving.ServedEntityOutput, len(updateReq.ServedEntities)) + for i, entity := range updateReq.ServedEntities { + config.ServedEntities[i] = serving.ServedEntityOutput{ + EntityName: entity.EntityName, + EntityVersion: entity.EntityVersion, + EnvironmentVars: entity.EnvironmentVars, + ExternalModel: entity.ExternalModel, + InstanceProfileArn: entity.InstanceProfileArn, + MaxProvisionedConcurrency: entity.MaxProvisionedConcurrency, + MaxProvisionedThroughput: entity.MaxProvisionedThroughput, + MinProvisionedConcurrency: entity.MinProvisionedConcurrency, + MinProvisionedThroughput: entity.MinProvisionedThroughput, + Name: entity.Name, + ProvisionedModelUnits: entity.ProvisionedModelUnits, + ScaleToZeroEnabled: entity.ScaleToZeroEnabled, + WorkloadSize: entity.WorkloadSize, + WorkloadType: entity.WorkloadType, + ForceSendFields: entity.ForceSendFields, + } + } + } + + // Convert ServedModelInput to ServedModelOutput + if len(updateReq.ServedModels) > 0 { + config.ServedModels = make([]serving.ServedModelOutput, len(updateReq.ServedModels)) + for i, model := range updateReq.ServedModels { + config.ServedModels[i] = serving.ServedModelOutput{ + EnvironmentVars: model.EnvironmentVars, + InstanceProfileArn: model.InstanceProfileArn, + MaxProvisionedConcurrency: model.MaxProvisionedConcurrency, + MinProvisionedConcurrency: model.MinProvisionedConcurrency, + ModelName: model.ModelName, + ModelVersion: model.ModelVersion, + Name: model.Name, + ProvisionedModelUnits: model.ProvisionedModelUnits, + ScaleToZeroEnabled: model.ScaleToZeroEnabled, + WorkloadSize: model.WorkloadSize, + WorkloadType: serving.ServingModelWorkloadType(model.WorkloadType), + ForceSendFields: model.ForceSendFields, + } + } + } + + // Convert AutoCaptureConfig if present + if updateReq.AutoCaptureConfig != nil { + config.AutoCaptureConfig = &serving.AutoCaptureConfigOutput{ + CatalogName: updateReq.AutoCaptureConfig.CatalogName, + SchemaName: updateReq.AutoCaptureConfig.SchemaName, + TableNamePrefix: updateReq.AutoCaptureConfig.TableNamePrefix, + Enabled: updateReq.AutoCaptureConfig.Enabled, + ForceSendFields: updateReq.AutoCaptureConfig.ForceSendFields, + } + } + } + + endpoint.Config = config + endpoint.State = &serving.EndpointState{ + ConfigUpdate: serving.EndpointStateConfigUpdateNotUpdating, + } + + s.ServingEndpoints[name] = endpoint + + return Response{ + Body: endpoint, + } +} From 596ee57c76861c6827f60c29c2c80551eb1a31a4 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 6 Nov 2025 15:50:45 +0100 Subject: [PATCH 02/26] update acceptance tests --- .../basic/databricks.yml.tmpl | 2 +- .../basic/out.requests.txt | 88 -------------- .../basic/out.test.toml | 2 +- .../model_serving_endpoints/basic/output.txt | 70 +++++++++-- .../model_serving_endpoints/basic/script | 18 ++- .../model_serving_endpoints/basic/test.toml | 5 +- .../recreate/catalog-name/databricks.yml.tmpl | 23 ++++ .../out.first-plan.direct-exp.json | 32 +++++ .../out.first-plan.terraform.json | 7 ++ .../out.second-plan.direct-exp.json | 39 ++++++ .../out.second-plan.terraform.json | 7 ++ .../recreate/catalog-name/out.test.toml | 5 + .../recreate/catalog-name/output.txt | 93 ++++++++++++++ .../recreate/catalog-name/script | 25 ++++ .../recreate/catalog-name/test.toml | 7 ++ .../recreate/name-change/databricks.yml.tmpl | 19 +++ .../out.first-plan.direct-exp.json | 27 +++++ .../name-change/out.first-plan.terraform.json | 7 ++ .../out.second-plan.direct-exp.json | 34 ++++++ .../out.second-plan.terraform.json | 7 ++ .../recreate/name-change/out.test.toml | 5 + .../recreate/name-change/output.txt | 83 +++++++++++++ .../recreate/name-change/script | 25 ++++ .../route-optimized/databricks.yml.tmpl | 20 ++++ .../out.first-plan.direct-exp.json | 28 +++++ .../out.first-plan.terraform.json | 7 ++ .../out.second-plan.direct-exp.json | 35 ++++++ .../out.second-plan.terraform.json | 7 ++ .../recreate/route-optimized/out.test.toml | 5 + .../recreate/route-optimized/output.txt | 84 +++++++++++++ .../recreate/route-optimized/script | 25 ++++ .../recreate/schema-name/databricks.yml.tmpl | 23 ++++ .../out.first-plan.direct-exp.json | 32 +++++ .../schema-name/out.first-plan.terraform.json | 7 ++ .../out.second-plan.direct-exp.json | 39 ++++++ .../out.second-plan.terraform.json | 7 ++ .../recreate/schema-name/out.test.toml | 5 + .../recreate/schema-name/output.txt | 93 ++++++++++++++ .../recreate/schema-name/script | 25 ++++ .../recreate/table-prefix/databricks.yml.tmpl | 23 ++++ .../out.first-plan.direct-exp.json | 32 +++++ .../out.first-plan.terraform.json | 7 ++ .../out.second-plan.direct-exp.json | 39 ++++++ .../out.second-plan.terraform.json | 7 ++ .../recreate/table-prefix/out.test.toml | 5 + .../recreate/table-prefix/output.txt | 93 ++++++++++++++ .../recreate/table-prefix/script | 25 ++++ .../recreate/test.toml | 7 ++ .../databricks.yml.tmpl | 6 +- .../running-endpoint/out.plan.direct-exp.txt | 23 ++++ .../running-endpoint/out.plan.terraform.txt | 7 ++ .../out.test.toml | 0 .../running-endpoint/output.txt | 43 +++++++ .../running-endpoint/script | 17 +++ .../running-endpoint/test.toml | 15 +++ .../update/description/databricks.yml.tmpl | 20 ++++ .../out.first-plan.direct-exp.json | 28 +++++ .../description/out.first-plan.terraform.json | 7 ++ .../out.second-plan.direct-exp.json | 59 +++++++++ .../out.second-plan.terraform.json | 7 ++ .../update/description/out.test.toml | 5 + .../update/description/output.txt | 58 +++++++++ .../update/description/script | 23 ++++ .../model_serving_endpoints/update/output.txt | 113 ------------------ .../model_serving_endpoints/update/script | 37 ------ .../served-entities/databricks.yml.tmpl | 19 +++ .../out.first-plan.direct-exp.json | 27 +++++ .../out.first-plan.terraform.json | 7 ++ .../out.second-plan.direct-exp.json | 57 +++++++++ .../out.second-plan.terraform.json | 7 ++ .../update/served-entities/out.test.toml | 5 + .../update/served-entities/output.txt | 76 ++++++++++++ .../update/served-entities/script | 23 ++++ .../update/tags/databricks.yml.tmpl | 24 ++++ .../tags/out.first-plan.direct-exp.json | 37 ++++++ .../update/tags/out.first-plan.terraform.json | 7 ++ .../tags/out.second-plan.direct-exp.json | 77 ++++++++++++ .../tags/out.second-plan.terraform.json | 7 ++ .../update/tags/out.test.toml | 5 + .../update/tags/output.txt | 50 ++++++++ .../update/tags/script | 23 ++++ .../model_serving_endpoints/update/test.toml | 9 +- .../dresources/model_serving_endpoint.go | 12 ++ 83 files changed, 1994 insertions(+), 256 deletions(-) delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/test.toml rename acceptance/bundle/resources/model_serving_endpoints/{update => running-endpoint}/databricks.yml.tmpl (67%) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt create mode 100644 acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.terraform.txt rename acceptance/bundle/resources/model_serving_endpoints/{update => running-endpoint}/out.test.toml (100%) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt create mode 100644 acceptance/bundle/resources/model_serving_endpoints/running-endpoint/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/update/description/script delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/output.txt delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct-exp.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml create mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt create mode 100755 acceptance/bundle/resources/model_serving_endpoints/update/tags/script diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl index 97ce316da7..1e113e4010 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl @@ -4,4 +4,4 @@ bundle: resources: model_serving_endpoints: my_endpoint: - name: test-endpoint-$UNIQUE_NAME + name: $ENDPOINT_NAME diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt deleted file mode 100644 index 0041483871..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.requests.txt +++ /dev/null @@ -1,88 +0,0 @@ -{ - "method": "GET", - "path": "/api/2.0/preview/scim/v2/Me" -} -{ - "method": "GET", - "path": "/api/2.0/preview/scim/v2/Me" -} -{ - "method": "GET", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" -} -{ - "method": "GET", - "path": "/api/2.0/preview/scim/v2/Me" -} -{ - "method": "GET", - "path": "/api/2.0/workspace/get-status", - "q": { - "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default" - } -} -{ - "method": "POST", - "path": "/api/2.0/workspace-files/import-file/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock", - "q": { - "overwrite": "false" - }, - "body": { - "ID": "[UUID]", - "AcquisitionTime": "[TIMESTAMP]", - "IsForced": false, - "User": "[USERNAME]" - } -} -{ - "method": "GET", - "path": "/api/2.0/workspace/get-status", - "q": { - "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock", - "return_export_info": "true" - } -} -{ - "method": "GET", - "path": "/api/2.0/workspace/export", - "q": { - "direct_download": "true", - "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock" - } -} -{ - "method": "GET", - "path": "/api/2.0/workspace/get-status", - "q": { - "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/terraform.tfstate", - "return_export_info": "true" - } -} -{ - "method": "GET", - "path": "/api/2.0/workspace-files/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/terraform.tfstate" -} -{ - "method": "GET", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" -} -{ - "method": "DELETE", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" -} -{ - "method": "POST", - "path": "/api/2.0/workspace/delete", - "body": { - "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default", - "recursive": true - } -} -{ - "method": "GET", - "path": "/api/2.0/workspace/get-status", - "q": { - "path": "/Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/state/deploy.lock", - "return_export_info": "true" - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml index 43c8f792f5..24581c18a4 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = true [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt index cf636e82f8..158c3abda2 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt @@ -1,4 +1,18 @@ +>>> [CLI] bundle debug plan +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "create", + "new_state": { + "config": { + "name": "[ENDPOINT_NAME_1]" + } + } + } + } +} + >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... Deploying resources... @@ -10,20 +24,62 @@ Deployment complete! "method": "POST", "path": "/api/2.0/serving-endpoints", "body": { - "name": "[ENDPOINT_ID]" + "name": "[ENDPOINT_NAME_1]" } } ->>> [CLI] serving-endpoints get [ENDPOINT_ID] +>>> [CLI] serving-endpoints get [ENDPOINT_NAME_1] +{ + "name": "[ENDPOINT_NAME_1]", + "creator": "[USERNAME]" +} + +>>> [CLI] bundle debug plan +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "name": "[ENDPOINT_NAME_2]" + } + }, + "changes": { + "local": { + "name": { + "action": "recreate" + } + } + } + } + } +} + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_NAME_1]" +} { - "creator":"[USERNAME]", - "id":"[UUID]", - "name":"[ENDPOINT_ID]", - "state": { - "config_update":"NOT_UPDATING" + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "name": "[ENDPOINT_NAME_2]" } } +>>> [CLI] serving-endpoints get [ENDPOINT_NAME_2] +{ + "name": "[ENDPOINT_NAME_2]", + "creator": "[USERNAME]" +} + >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: delete model_serving_endpoint my_endpoint diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/script b/acceptance/bundle/resources/model_serving_endpoints/basic/script index 0f208977ae..fbad27f16e 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/script +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/script @@ -1,15 +1,27 @@ +export ENDPOINT_NAME="test-endpoint-$UNIQUE_NAME-1" + envsubst < databricks.yml.tmpl > databricks.yml cleanup() { trace $CLI bundle destroy --auto-approve + rm out.requests.txt } trap cleanup EXIT +trace $CLI bundle debug plan trace $CLI bundle deploy trace print_requests.py //serving-endpoints -endpoint_name=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.my_endpoint.name') -echo "$endpoint_name:ENDPOINT_ID" >> ACC_REPLS +echo "$ENDPOINT_NAME:ENDPOINT_NAME_1" >> ACC_REPLS +trace $CLI serving-endpoints get $ENDPOINT_NAME | jq '{name, creator}' + +export ENDPOINT_NAME="test-endpoint-$UNIQUE_NAME-2" +envsubst < databricks.yml.tmpl > databricks.yml +trace $CLI bundle debug plan +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints -trace $CLI serving-endpoints get $endpoint_name +echo "$ENDPOINT_NAME:ENDPOINT_NAME_2" >> ACC_REPLS +trace $CLI serving-endpoints get $ENDPOINT_NAME | jq '{name, creator}' diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml index c72905b0fe..24581c18a4 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml @@ -1,2 +1,5 @@ -Timeout = '5m' +Local = true Cloud = true + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/databricks.yml.tmpl new file mode 100644 index 0000000000..5cb6657fd5 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/databricks.yml.tmpl @@ -0,0 +1,23 @@ +bundle: + name: test-mse-catalog-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" + auto_capture_config: + catalog_name: main + schema_name: default + table_name_prefix: my_table diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..11afeef35c --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct-exp.json @@ -0,0 +1,32 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..156261de42 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json @@ -0,0 +1,39 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "other_catalog", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + }, + "changes": { + "local": { + "config.auto_capture_config.catalog_name": { + "action": "recreate" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.terraform.json new file mode 100644 index 0000000000..225f2eb49a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt new file mode 100644 index 0000000000..311068305a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt @@ -0,0 +1,93 @@ + +>>> CLI bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"main" + +>>> update_file.py databricks.yml catalog_name: main catalog_name: other_catalog + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" +} +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "auto_capture_config": { + "catalog_name": "other_catalog", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"other_catalog" + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script new file mode 100755 index 0000000000..9221d02028 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script @@ -0,0 +1,25 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.auto_capture_config.catalog_name' + +trace update_file.py databricks.yml "catalog_name: main" "catalog_name: other_catalog" +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +NEW_ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$NEW_ENDPOINT_ID:NEW_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" | jq '.config.auto_capture_config.catalog_name' diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml new file mode 100644 index 0000000000..3aa4a19050 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml @@ -0,0 +1,7 @@ +Local = true +Cloud = false +RecordRequests = true + +Ignore = [ + "databricks.yml", +] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/databricks.yml.tmpl new file mode 100644 index 0000000000..fe2f5f02c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/databricks.yml.tmpl @@ -0,0 +1,19 @@ +bundle: + name: test-mse-name-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..ece5a9db9a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct-exp.json @@ -0,0 +1,27 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..fa0d636f23 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct-exp.json @@ -0,0 +1,34 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[NEW_ENDPOINT_ID]" + } + }, + "changes": { + "local": { + "name": { + "action": "recreate" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.terraform.json new file mode 100644 index 0000000000..225f2eb49a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt new file mode 100644 index 0000000000..662b3d488b --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt @@ -0,0 +1,83 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"[ORIGINAL_ENDPOINT_ID]" + +>>> update_file.py databricks.yml name: [ORIGINAL_ENDPOINT_ID] name: [NEW_ENDPOINT_ID] + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" +} +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[NEW_ENDPOINT_ID]" + } +} + +>>> [CLI] serving-endpoints get [NEW_ENDPOINT_ID] +"[NEW_ENDPOINT_ID]" + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script new file mode 100755 index 0000000000..e1fa06a187 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script @@ -0,0 +1,25 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.name' + +trace update_file.py databricks.yml "name: test-endpoint-$UNIQUE_NAME" "name: test-endpoint-2-$UNIQUE_NAME" +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +NEW_ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$NEW_ENDPOINT_ID:NEW_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" | jq '.name' diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl new file mode 100644 index 0000000000..1b1c65025b --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl @@ -0,0 +1,20 @@ +bundle: + name: test-mse-route-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" + route_optimized: false diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..5e8fd28ba0 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct-exp.json @@ -0,0 +1,28 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]", + "route_optimized": false + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..8025fe7468 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct-exp.json @@ -0,0 +1,35 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]", + "route_optimized": true + } + }, + "changes": { + "local": { + "route_optimized": { + "action": "recreate" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.terraform.json new file mode 100644 index 0000000000..225f2eb49a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt new file mode 100644 index 0000000000..cdda7bc15f --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt @@ -0,0 +1,84 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +null + +>>> update_file.py databricks.yml route_optimized: false route_optimized: true + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" +} +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]", + "route_optimized": true + } +} + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +true + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script new file mode 100755 index 0000000000..7c3a61726a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script @@ -0,0 +1,25 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.route_optimized' + +trace update_file.py databricks.yml "route_optimized: false" "route_optimized: true" +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +NEW_ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$NEW_ENDPOINT_ID:NEW_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" | jq '.route_optimized' diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/databricks.yml.tmpl new file mode 100644 index 0000000000..f57fcc4f03 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/databricks.yml.tmpl @@ -0,0 +1,23 @@ +bundle: + name: test-mse-schema-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" + auto_capture_config: + catalog_name: main + schema_name: default + table_name_prefix: my_table diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..11afeef35c --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct-exp.json @@ -0,0 +1,32 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..c1f8fa4b13 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct-exp.json @@ -0,0 +1,39 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "other_schema", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + }, + "changes": { + "local": { + "config.auto_capture_config.schema_name": { + "action": "recreate" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.terraform.json new file mode 100644 index 0000000000..225f2eb49a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt new file mode 100644 index 0000000000..acb761dd69 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt @@ -0,0 +1,93 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"default" + +>>> update_file.py databricks.yml schema_name: default schema_name: other_schema + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" +} +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "other_schema", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"other_schema" + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script new file mode 100755 index 0000000000..07b52b2dd5 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script @@ -0,0 +1,25 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.auto_capture_config.schema_name' + +trace update_file.py databricks.yml "schema_name: default" "schema_name: other_schema" +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +NEW_ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$NEW_ENDPOINT_ID:NEW_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" | jq '.config.auto_capture_config.schema_name' diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/databricks.yml.tmpl new file mode 100644 index 0000000000..8b22b900f6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/databricks.yml.tmpl @@ -0,0 +1,23 @@ +bundle: + name: test-mse-table-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" + auto_capture_config: + catalog_name: main + schema_name: default + table_name_prefix: my_table diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..11afeef35c --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json @@ -0,0 +1,32 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..c546a1392b --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct-exp.json @@ -0,0 +1,39 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "other_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + }, + "changes": { + "local": { + "config.auto_capture_config.table_name_prefix": { + "action": "recreate" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.terraform.json new file mode 100644 index 0000000000..225f2eb49a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt new file mode 100644 index 0000000000..dd102ba9fc --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt @@ -0,0 +1,93 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"my_table" + +>>> update_file.py databricks.yml table_name_prefix: my_table table_name_prefix: other_table + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" +} +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "other_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"other_table" + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script new file mode 100755 index 0000000000..d606520b3d --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script @@ -0,0 +1,25 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.auto_capture_config.table_name_prefix' + +trace update_file.py databricks.yml "table_name_prefix: my_table" "table_name_prefix: other_table" +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +NEW_ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$NEW_ENDPOINT_ID:NEW_ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" | jq '.config.auto_capture_config.table_name_prefix' diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/test.toml new file mode 100644 index 0000000000..3aa4a19050 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/test.toml @@ -0,0 +1,7 @@ +Local = true +Cloud = false +RecordRequests = true + +Ignore = [ + "databricks.yml", +] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/databricks.yml.tmpl similarity index 67% rename from acceptance/bundle/resources/model_serving_endpoints/update/databricks.yml.tmpl rename to acceptance/bundle/resources/model_serving_endpoints/running-endpoint/databricks.yml.tmpl index e5e4a7c31d..8318961574 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/databricks.yml.tmpl +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/databricks.yml.tmpl @@ -7,8 +7,8 @@ resources: name: test-endpoint-$UNIQUE_NAME config: served_entities: - - name: test-entity - entity_name: my-model - entity_version: "1" + - name: "llama" + entity_name: system.ai.llama_v3_2_1b_instruct + entity_version: 1 workload_size: Small scale_to_zero_enabled: true diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt new file mode 100644 index 0000000000..2987b73b41 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt @@ -0,0 +1,23 @@ +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "entity_name": "system.ai.llama_v3_2_1b_instruct", + "entity_version": "1", + "name": "llama", + "scale_to_zero_enabled": true, + "workload_size": "Small" + } + ] + }, + "name": "test-endpoint-[UNIQUE_NAME]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.terraform.txt b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.terraform.txt new file mode 100644 index 0000000000..1d92fcbea4 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.terraform.txt @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/update/out.test.toml rename to acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt new file mode 100644 index 0000000000..e462a58700 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt @@ -0,0 +1,43 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "entity_name": "system.ai.llama_v3_2_1b_instruct", + "entity_version": "1", + "name": "llama", + "scale_to_zero_enabled": true, + "workload_size": "Small" + } + ] + }, + "name": "test-endpoint-[UNIQUE_NAME]" + } +} + +>>> [CLI] serving-endpoints get test-endpoint-[UNIQUE_NAME] +{ + "name": "test-endpoint-[UNIQUE_NAME]", + "creator": "[USERNAME]" +} + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint my_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/script b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/script new file mode 100644 index 0000000000..fab464d557 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/script @@ -0,0 +1,17 @@ +export VERSION=$($CLI model-versions list system.ai.llama_v3_2_1b_instruct | jq '.[0].version') + +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.plan.$DATABRICKS_BUNDLE_ENGINE.txt +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +endpoint_name=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.my_endpoint.name') +trace $CLI serving-endpoints get $endpoint_name | jq '{name, creator}' diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml new file mode 100644 index 0000000000..5708de9cac --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml @@ -0,0 +1,15 @@ +# Timeout = '5m' +Cloud = true +Local = true + +[[Server]] +Pattern = "GET /api/2.1/unity-catalog/models/system.ai.llama_v3_2_1b_instruct/versions" +Response.Body = ''' +{ + "model_versions": [ + { + "version": 1 + } + ] +} +''' diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl new file mode 100644 index 0000000000..3ddd574a40 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl @@ -0,0 +1,20 @@ +bundle: + name: test-mse-desc-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + description: "Initial description" + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..5982335082 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct-exp.json @@ -0,0 +1,28 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "description": "Initial description", + "name": "[ENDPOINT_ID]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..b65bf69e7a --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct-exp.json @@ -0,0 +1,59 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "update", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "description": "Updated description", + "name": "[ENDPOINT_ID]" + } + }, + "remote_state": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "creator": "[USERNAME]", + "description": "Initial description", + "id": "[UUID]", + "name": "[ENDPOINT_ID]", + "state": { + "config_update": "NOT_UPDATING" + } + }, + "changes": { + "local": { + "description": { + "action": "update" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json new file mode 100644 index 0000000000..bc939a91b2 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "update" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt new file mode 100644 index 0000000000..2875cffdd8 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt @@ -0,0 +1,58 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +"Initial description" + +>>> update_file.py databricks.yml description: "Initial description" description: "Updated description" + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "description": "Initial description", + "name": "[ENDPOINT_ID]" + } +} + +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +"Initial description" + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/script b/acceptance/bundle/resources/model_serving_endpoints/update/description/script new file mode 100755 index 0000000000..7a60b33cb2 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/script @@ -0,0 +1,23 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.description' + +trace update_file.py databricks.yml 'description: "Initial description"' 'description: "Updated description"' +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.description' diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/output.txt deleted file mode 100644 index ef5069306d..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/output.txt +++ /dev/null @@ -1,113 +0,0 @@ - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... -Deploying resources... -Updating deployment state... -Deployment complete! - -=== Initial deployment ->>> print_requests -{ - "body": { - "config": { - "served_entities": [ - { - "entity_name": "my-model", - "entity_version": "1", - "name": "test-entity", - "scale_to_zero_enabled": true, - "workload_size": "Small" - } - ] - }, - "name": "[ENDPOINT_ID]" - }, - "method": "POST", - "path": "/api/2.0/serving-endpoints" -} - -=== Update the endpoint configuration (change model version) ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> print_requests -{ - "body": { - "served_entities": [ - { - "entity_name": "my-model", - "entity_version": "2", - "name": "test-entity", - "scale_to_zero_enabled": true, - "workload_size": "Small" - } - ] - }, - "method": "PUT", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config" -} - -=== Verify the endpoint was updated (not recreated) ->>> [CLI] serving-endpoints get [ENDPOINT_ID] -{ - "config": { - "served_entities": [ - { - "entity_name":"my-model", - "entity_version":"2", - "name":"test-entity", - "scale_to_zero_enabled":true, - "workload_size":"Small" - } - ] - }, - "creator":"[USERNAME]", - "id":"[UUID]", - "name":"[ENDPOINT_ID]", - "state": { - "config_update":"NOT_UPDATING" - } -} - -=== Verify endpoint is in NOT_UPDATING state after update -Endpoint is in NOT_UPDATING state - -=== Verify the model version was updated -Model version updated to 2 - -=== Destroy the endpoint ->>> [CLI] bundle destroy --auto-approve -The following resources will be deleted: - delete model_serving_endpoint my_endpoint - -All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default - -Deleting files... -Destroy complete! - ->>> print_requests -{ - "body": { - "served_entities": [ - { - "entity_name": "my-model", - "entity_version": "2", - "name": "test-entity", - "scale_to_zero_enabled": true, - "workload_size": "Small" - } - ] - }, - "method": "PUT", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config" -} -{ - "method": "DELETE", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" -} - ->>> musterr [CLI] serving-endpoints get [ENDPOINT_ID] -Error: Serving endpoint with name [ENDPOINT_ID] not found diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/script b/acceptance/bundle/resources/model_serving_endpoints/update/script deleted file mode 100644 index 3cb25bc932..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/script +++ /dev/null @@ -1,37 +0,0 @@ -envsubst < databricks.yml.tmpl > databricks.yml -trace $CLI bundle deploy - -print_requests() { - jq --sort-keys 'select(.method != "GET" and (.path | contains("/serving-endpoints")))' < out.requests.txt -} - -title "Initial deployment" -trace print_requests - -endpoint_name=$(jq -r 'select(.method == "POST" and (.path | contains("/serving-endpoints"))) | .body.name' < out.requests.txt | head -1) -echo "$endpoint_name:ENDPOINT_ID" >> ACC_REPLS -rm out.requests.txt - -title "Update the endpoint configuration (change model version)" -update_file.py databricks.yml "1" "2" -trace $CLI bundle deploy -trace print_requests - -title "Verify the endpoint was updated (not recreated)" -trace $CLI serving-endpoints get $endpoint_name - -title "Verify endpoint is in NOT_UPDATING state after update" -$CLI serving-endpoints get $endpoint_name | jq -r '.state.config_update' | grep -q 'NOT_UPDATING' -echo "" -echo "Endpoint is in NOT_UPDATING state" - -title "Verify the model version was updated" -$CLI serving-endpoints get $endpoint_name | jq -r '.config.served_entities[0].entity_version' | grep -q '2' -echo "" -echo "Model version updated to 2" - -title "Destroy the endpoint" -trace $CLI bundle destroy --auto-approve -trace print_requests -trace musterr $CLI serving-endpoints get $endpoint_name -rm out.requests.txt diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl new file mode 100644 index 0000000000..c1c7a26545 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl @@ -0,0 +1,19 @@ +bundle: + name: test-mse-entities-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..31f4ab3a45 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct-exp.json @@ -0,0 +1,27 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ENDPOINT_ID]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..b2c75feacb --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct-exp.json @@ -0,0 +1,57 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "update", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ENDPOINT_ID]" + } + }, + "remote_state": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "creator": "[USERNAME]", + "id": "[UUID]", + "name": "[ENDPOINT_ID]", + "state": { + "config_update": "NOT_UPDATING" + } + }, + "changes": { + "local": { + "config.served_entities[0].external_model.name": { + "action": "update" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json new file mode 100644 index 0000000000..bc939a91b2 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "update" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt new file mode 100644 index 0000000000..954dd0deb7 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt @@ -0,0 +1,76 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +"gpt-4o-mini" + +>>> update_file.py databricks.yml name: gpt-4o-mini name: gpt-4o + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ENDPOINT_ID]" + } +} +{ + "method": "PUT", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config", + "body": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + } +} + +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +"gpt-4o" + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script new file mode 100755 index 0000000000..0092bddace --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script @@ -0,0 +1,23 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.served_entities[0].external_model.name' + +trace update_file.py databricks.yml 'name: gpt-4o-mini' 'name: gpt-4o' +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.served_entities[0].external_model.name' diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl new file mode 100644 index 0000000000..a8c8597fc5 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl @@ -0,0 +1,24 @@ +bundle: + name: test-mse-tags-$UNIQUE_NAME + +workspace: + root_path: ~/.bundle/$UNIQUE_NAME + +resources: + model_serving_endpoints: + test_endpoint: + name: test-endpoint-$UNIQUE_NAME + config: + served_entities: + - name: prod + external_model: + name: gpt-4o-mini + provider: openai + task: llm/v1/chat + openai_config: + openai_api_key: "{{secrets/test-scope/openai-key}}" + tags: + - key: env + value: test + - key: team + value: ml diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct-exp.json new file mode 100644 index 0000000000..156d35a4b9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct-exp.json @@ -0,0 +1,37 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ENDPOINT_ID]", + "tags": [ + { + "key": "env", + "value": "test" + }, + { + "key": "team", + "value": "ml" + } + ] + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json new file mode 100644 index 0000000000..82ea9497c9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct-exp.json new file mode 100644 index 0000000000..0a57ab0433 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct-exp.json @@ -0,0 +1,77 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "update", + "new_state": { + "config": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ENDPOINT_ID]", + "tags": [ + { + "key": "env", + "value": "prod" + }, + { + "key": "team", + "value": "ml" + } + ] + } + }, + "remote_state": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "creator": "[USERNAME]", + "id": "[UUID]", + "name": "[ENDPOINT_ID]", + "state": { + "config_update": "NOT_UPDATING" + }, + "tags": [ + { + "key": "env", + "value": "test" + }, + { + "key": "team", + "value": "ml" + } + ] + }, + "changes": { + "local": { + "tags[0].value": { + "action": "update" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json new file mode 100644 index 0000000000..bc939a91b2 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "update" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml new file mode 100644 index 0000000000..e092fd5ed6 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml @@ -0,0 +1,5 @@ +Local = true +Cloud = false + +[EnvMatrix] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt new file mode 100644 index 0000000000..687ebd464c --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt @@ -0,0 +1,50 @@ + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +[ + { + "key": "env", + "value": "test" + }, + { + "key": "team", + "value": "ml" + } +] + +>>> update_file.py databricks.yml value: test value: prod + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Error: terraform apply: exit status 1 + +Error: cannot update model serving: No stub found for pattern: PATCH /api/2.0/serving-endpoints/[ENDPOINT_ID]/tags + + with databricks_model_serving.test_endpoint, + on bundle.tf.json line 42, in resource.databricks_model_serving.test_endpoint: + 42: } + + + +Updating deployment state... + +>>> [CLI] bundle destroy --auto-approve +The following resources will be deleted: + delete model_serving_endpoint test_endpoint + +All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] + +Deleting files... +Destroy complete! + +Exit code: 1 diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/script b/acceptance/bundle/resources/model_serving_endpoints/update/tags/script new file mode 100755 index 0000000000..42172e8af8 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/script @@ -0,0 +1,23 @@ +#!/bin/bash +envsubst < databricks.yml.tmpl > databricks.yml + +cleanup() { + trace $CLI bundle destroy --auto-approve + rm -f out.requests.txt +} +trap cleanup EXIT + +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') +echo "$ENDPOINT_ID:ENDPOINT_ID" >> ACC_REPLS +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.tags' + +trace update_file.py databricks.yml 'value: test' 'value: prod' +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle deploy + +trace print_requests.py //serving-endpoints + +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.tags' diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/test.toml index c72905b0fe..3aa4a19050 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/update/test.toml @@ -1,2 +1,7 @@ -Timeout = '5m' -Cloud = true +Local = true +Cloud = false +RecordRequests = true + +Ignore = [ + "databricks.yml", +] diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index 18b172a60e..258c2f38a3 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -5,6 +5,7 @@ import ( "time" "github.com/databricks/cli/bundle/config/resources" + "github.com/databricks/cli/bundle/deployplan" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/service/serving" ) @@ -142,3 +143,14 @@ func (r *ResourceModelServingEndpoint) WaitAfterUpdate(ctx context.Context, conf func (r *ResourceModelServingEndpoint) DoDelete(ctx context.Context, id string) error { return r.client.ServingEndpoints.DeleteByName(ctx, id) } + +func (*ResourceModelServingEndpoint) FieldTriggers(_ bool) map[string]deployplan.ActionType { + // TF implementation: https://github.com/databricks/terraform-provider-databricks/blob/6c106e8e7052bb2726148d66309fd460ed444236/mlflow/resource_mlflow_experiment.go#L22 + return map[string]deployplan.ActionType{ + "name": deployplan.ActionTypeRecreate, + "config.auto_capture_config.catalog_name": deployplan.ActionTypeRecreate, + "config.auto_capture_config.schema_name": deployplan.ActionTypeRecreate, + "config.auto_capture_config.table_name_prefix": deployplan.ActionTypeRecreate, + "route_optimized": deployplan.ActionTypeRecreate, + } +} From b0c4760a2a0c5ee2fa79d41671289ddb5141d964 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 6 Nov 2025 16:02:50 +0100 Subject: [PATCH 03/26] ipdate the test --- .../running-endpoint/databricks.yml.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/databricks.yml.tmpl index 8318961574..73e0943152 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/databricks.yml.tmpl +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/databricks.yml.tmpl @@ -9,6 +9,6 @@ resources: served_entities: - name: "llama" entity_name: system.ai.llama_v3_2_1b_instruct - entity_version: 1 + entity_version: $VERSION workload_size: Small scale_to_zero_enabled: true From 56e675f23437b3ca291956b51ab92feab2639c02 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 6 Nov 2025 16:54:42 +0100 Subject: [PATCH 04/26] proper replacements --- .../running-endpoint/out.plan.direct-exp.txt | 2 +- .../model_serving_endpoints/running-endpoint/output.txt | 2 +- .../model_serving_endpoints/running-endpoint/test.toml | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt index 2987b73b41..3539d2da56 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt @@ -8,7 +8,7 @@ "served_entities": [ { "entity_name": "system.ai.llama_v3_2_1b_instruct", - "entity_version": "1", + "entity_version": "[VERSION]", "name": "llama", "scale_to_zero_enabled": true, "workload_size": "Small" diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt index e462a58700..1fd0ab1517 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/output.txt @@ -16,7 +16,7 @@ Deployment complete! "served_entities": [ { "entity_name": "system.ai.llama_v3_2_1b_instruct", - "entity_version": "1", + "entity_version": "[VERSION]", "name": "llama", "scale_to_zero_enabled": true, "workload_size": "Small" diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml index 5708de9cac..8afe51c16f 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml @@ -13,3 +13,7 @@ Response.Body = ''' ] } ''' + +[[Repls]] +Old = '"entity_version": "1",' +New = '"entity_version": "[VERSION]",' From b33549be158cba43b100763af45673337dca3ffa Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 6 Nov 2025 18:42:56 +0100 Subject: [PATCH 05/26] make permissions work --- .../basic/databricks.yml.tmpl | 6 + .../basic/out.test.toml | 2 +- .../model_serving_endpoints/basic/output.txt | 119 +++++++++++++++++- .../model_serving_endpoints/basic/script | 26 +++- .../model_serving_endpoints/basic/test.toml | 3 +- .../recreate/catalog-name/out.test.toml | 2 +- .../recreate/name-change/out.test.toml | 2 +- .../recreate/route-optimized/out.test.toml | 2 +- .../recreate/schema-name/out.test.toml | 2 +- .../recreate/table-prefix/out.test.toml | 2 +- .../running-endpoint/out.test.toml | 2 +- .../update/description/out.test.toml | 2 +- .../update/served-entities/out.test.toml | 2 +- .../update/tags/out.test.toml | 2 +- .../dresources/model_serving_endpoint.go | 52 +++++--- bundle/direct/dresources/permissions.go | 11 +- 16 files changed, 203 insertions(+), 34 deletions(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl index 1e113e4010..a29c77bdf2 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl @@ -5,3 +5,9 @@ resources: model_serving_endpoints: my_endpoint: name: $ENDPOINT_NAME + + permissions: + - level: CAN_VIEW + user_name: $TEST_USER_EMAIL + - level: CAN_MANAGE + user_name: $CURRENT_USER_NAME diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml index 24581c18a4..19b2c349a3 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = true [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt index 158c3abda2..2d6a0b4838 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt @@ -9,6 +9,33 @@ "name": "[ENDPOINT_NAME_1]" } } + }, + "resources.model_serving_endpoints.my_endpoint.permissions": { + "depends_on": [ + { + "node": "resources.model_serving_endpoints.my_endpoint", + "label": "${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + ], + "action": "create", + "new_state": { + "config": { + "object_id": "", + "permissions": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "user_name": "[USERNAME]" + } + ] + }, + "vars": { + "object_id": "/serving-endpoints/${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + } } } } @@ -27,8 +54,24 @@ Deployment complete! "name": "[ENDPOINT_NAME_1]" } } +{ + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_1]", + "body": { + "access_control_list": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "user_name": "[USERNAME]" + } + ] + } +} ->>> [CLI] serving-endpoints get [ENDPOINT_NAME_1] +=== Print the GET endpoint details { "name": "[ENDPOINT_NAME_1]", "creator": "[USERNAME]" @@ -51,6 +94,62 @@ Deployment complete! } } } + }, + "resources.model_serving_endpoints.my_endpoint.permissions": { + "depends_on": [ + { + "node": "resources.model_serving_endpoints.my_endpoint", + "label": "${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + ], + "action": "update", + "new_state": { + "config": { + "object_id": "", + "permissions": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "user_name": "[USERNAME]" + } + ] + }, + "vars": { + "object_id": "/serving-endpoints/${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + }, + "remote_state": { + "object_id": "/serving-endpoints/[ENDPOINT_ID_1]", + "permissions": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + } + ] + }, + "changes": { + "local": { + "object_id": { + "action": "update" + } + }, + "remote": { + "permissions[1].service_principal_name": { + "action": "skip", + "reason": "server_side_default" + }, + "permissions[1].user_name": { + "action": "update" + } + } + } } } } @@ -73,8 +172,24 @@ Deployment complete! "name": "[ENDPOINT_NAME_2]" } } +{ + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_2]", + "body": { + "access_control_list": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "user_name": "[USERNAME]" + } + ] + } +} ->>> [CLI] serving-endpoints get [ENDPOINT_NAME_2] +=== Print the GET endpoint details { "name": "[ENDPOINT_NAME_2]", "creator": "[USERNAME]" diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/script b/acceptance/bundle/resources/model_serving_endpoints/basic/script index fbad27f16e..3f94168e9e 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/script +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/script @@ -1,5 +1,11 @@ export ENDPOINT_NAME="test-endpoint-$UNIQUE_NAME-1" +if [ -z "$CLOUD_ENV" ]; then + export TEST_USER_EMAIL="viewer@databricks.com" +fi + +echo "$TEST_USER_EMAIL:TEST_USER_EMAIL" >> ACC_REPLS + envsubst < databricks.yml.tmpl > databricks.yml cleanup() { @@ -10,18 +16,30 @@ trap cleanup EXIT trace $CLI bundle debug plan trace $CLI bundle deploy - trace print_requests.py //serving-endpoints echo "$ENDPOINT_NAME:ENDPOINT_NAME_1" >> ACC_REPLS -trace $CLI serving-endpoints get $ENDPOINT_NAME | jq '{name, creator}' + +# Record the endpoint ID for the first endpoint +get_output=$($CLI serving-endpoints get $ENDPOINT_NAME) +endpoint_id=$(echo "$get_output" | jq -r '.id') +echo "$endpoint_id:ENDPOINT_ID_1" >> ACC_REPLS + +title "Print the GET endpoint details\n" +echo "$get_output" | jq '{name, creator}' export ENDPOINT_NAME="test-endpoint-$UNIQUE_NAME-2" envsubst < databricks.yml.tmpl > databricks.yml trace $CLI bundle debug plan trace $CLI bundle deploy - trace print_requests.py //serving-endpoints echo "$ENDPOINT_NAME:ENDPOINT_NAME_2" >> ACC_REPLS -trace $CLI serving-endpoints get $ENDPOINT_NAME | jq '{name, creator}' + +# Record the endpoint ID for the second endpoint +get_output=$($CLI serving-endpoints get $ENDPOINT_NAME) +endpoint_id=$(echo "$get_output" | jq -r '.id') +echo "$endpoint_id:ENDPOINT_ID_2" >> ACC_REPLS + +title "Print the GET endpoint details\n" +echo "$get_output" | jq '{name, creator}' diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml index 24581c18a4..30fe47d72d 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml @@ -1,5 +1,6 @@ Local = true Cloud = true +RecordRequests = true [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml index 43c8f792f5..01ed6822af 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = true [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml index e092fd5ed6..d560f1de04 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml @@ -2,4 +2,4 @@ Local = true Cloud = false [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct-exp"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index f81fe174d6..9e1f293016 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -77,24 +77,37 @@ func configOutputToInput(output *serving.EndpointCoreConfigOutput) *serving.Endp } // TODO: Remap served_models to served_entities. -func (*ResourceModelServingEndpoint) RemapState(endpoint *serving.ServingEndpointDetailed) *serving.CreateServingEndpoint { +func (*ResourceModelServingEndpoint) RemapState(state *RefreshOutput) *serving.CreateServingEndpoint { + details := state.EndpointDetails // Map the remote state (ServingEndpointDetailed) to the local state (CreateServingEndpoint) // for proper comparison during diff calculation return &serving.CreateServingEndpoint{ - AiGateway: endpoint.AiGateway, - BudgetPolicyId: endpoint.BudgetPolicyId, - Config: configOutputToInput(endpoint.Config), - Description: endpoint.Description, - EmailNotifications: endpoint.EmailNotifications, - Name: endpoint.Name, - RouteOptimized: endpoint.RouteOptimized, - Tags: endpoint.Tags, - ForceSendFields: utils.FilterFields[serving.CreateServingEndpoint](endpoint.ForceSendFields), + AiGateway: details.AiGateway, + BudgetPolicyId: details.BudgetPolicyId, + Config: configOutputToInput(details.Config), + Description: details.Description, + EmailNotifications: details.EmailNotifications, + Name: details.Name, + RouteOptimized: details.RouteOptimized, + Tags: details.Tags, + ForceSendFields: utils.FilterFields[serving.CreateServingEndpoint](details.ForceSendFields), } } -func (r *ResourceModelServingEndpoint) DoRefresh(ctx context.Context, id string) (*serving.ServingEndpointDetailed, error) { - return r.client.ServingEndpoints.GetByName(ctx, id) +type RefreshOutput struct { + EndpointDetails *serving.ServingEndpointDetailed `json:"endpoint_details"` + EndpointId string `json:"endpoint_id"` +} + +func (r *ResourceModelServingEndpoint) DoRefresh(ctx context.Context, id string) (*RefreshOutput, error) { + endpoint, err := r.client.ServingEndpoints.GetByName(ctx, id) + if err != nil { + return nil, err + } + return &RefreshOutput{ + EndpointDetails: endpoint, + EndpointId: endpoint.Id, + }, nil } func (r *ResourceModelServingEndpoint) DoCreate(ctx context.Context, config *serving.CreateServingEndpoint) (string, error) { @@ -107,7 +120,7 @@ func (r *ResourceModelServingEndpoint) DoCreate(ctx context.Context, config *ser } // waitForEndpointReady waits for the serving endpoint to be ready (not updating) -func (r *ResourceModelServingEndpoint) waitForEndpointReady(ctx context.Context, name string) (*serving.ServingEndpointDetailed, error) { +func (r *ResourceModelServingEndpoint) waitForEndpointReady(ctx context.Context, name string) (*RefreshOutput, error) { waiter := &serving.WaitGetServingEndpointNotUpdating[serving.ServingEndpointDetailed]{ Response: &serving.ServingEndpointDetailed{Name: name}, Name: name, @@ -117,10 +130,17 @@ func (r *ResourceModelServingEndpoint) waitForEndpointReady(ctx context.Context, } // Model serving endpoints can take a long time to spin up. We match the timeout from TF here (35 minutes). - return waiter.GetWithTimeout(35 * time.Minute) + details, err := waiter.GetWithTimeout(35 * time.Minute) + if err != nil { + return nil, err + } + return &RefreshOutput{ + EndpointDetails: details, + EndpointId: details.Id, + }, nil } -func (r *ResourceModelServingEndpoint) WaitAfterCreate(ctx context.Context, config *serving.CreateServingEndpoint) (*serving.ServingEndpointDetailed, error) { +func (r *ResourceModelServingEndpoint) WaitAfterCreate(ctx context.Context, config *serving.CreateServingEndpoint) (*RefreshOutput, error) { return r.waitForEndpointReady(ctx, config.Name) } @@ -137,7 +157,7 @@ func (r *ResourceModelServingEndpoint) DoUpdate(ctx context.Context, id string, return err } -func (r *ResourceModelServingEndpoint) WaitAfterUpdate(ctx context.Context, config *serving.CreateServingEndpoint) (*serving.ServingEndpointDetailed, error) { +func (r *ResourceModelServingEndpoint) WaitAfterUpdate(ctx context.Context, config *serving.CreateServingEndpoint) (*RefreshOutput, error) { return r.waitForEndpointReady(ctx, config.Name) } diff --git a/bundle/direct/dresources/permissions.go b/bundle/direct/dresources/permissions.go index ce06a853a2..b6dbf4c6ad 100644 --- a/bundle/direct/dresources/permissions.go +++ b/bundle/direct/dresources/permissions.go @@ -57,13 +57,22 @@ func PreparePermissionsInputConfig(inputConfig any, node string) (*structvar.Str }) } + objectIdRef := prefix + "${" + baseNode + ".id}" + // For permissions, model serving endpoint uses it's internal ID, which is different + // from its CRUD APIs which use the name. + // We have a wrapper struct [RefreshOutput] from which we read the internal ID + // in order to set the appropriate permissions. + if strings.HasPrefix(baseNode, "resources.model_serving_endpoints.") { + objectIdRef = prefix + "${" + baseNode + ".endpoint_id}" + } + return &structvar.StructVar{ Config: &PermissionsState{ ObjectID: "", // Always a reference, defined in Refs below Permissions: permissions, }, Refs: map[string]string{ - "object_id": prefix + "${" + baseNode + ".id}", + "object_id": objectIdRef, }, }, nil } From a169e35bc80f92c23e52c2bc5cd5b4d164696125 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Thu, 6 Nov 2025 18:46:51 +0100 Subject: [PATCH 06/26] update to cloud only --- .../basic/databricks.yml.tmpl | 2 +- .../model_serving_endpoints/basic/out.test.toml | 2 +- .../model_serving_endpoints/basic/output.txt | 17 ++++------------- .../model_serving_endpoints/basic/test.toml | 2 +- 4 files changed, 7 insertions(+), 16 deletions(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl index a29c77bdf2..854624dcc0 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl @@ -10,4 +10,4 @@ resources: - level: CAN_VIEW user_name: $TEST_USER_EMAIL - level: CAN_MANAGE - user_name: $CURRENT_USER_NAME + service_principal_name: $CURRENT_USER_NAME diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml index 19b2c349a3..1ae6b5ffce 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml @@ -1,4 +1,4 @@ -Local = true +Local = false Cloud = true [EnvMatrix] diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt index 2d6a0b4838..d1723211ff 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt @@ -28,7 +28,7 @@ }, { "permission_level": "CAN_MANAGE", - "user_name": "[USERNAME]" + "service_principal_name": "[USERNAME]" } ] }, @@ -65,7 +65,7 @@ Deployment complete! }, { "permission_level": "CAN_MANAGE", - "user_name": "[USERNAME]" + "service_principal_name": "[USERNAME]" } ] } @@ -113,7 +113,7 @@ Deployment complete! }, { "permission_level": "CAN_MANAGE", - "user_name": "[USERNAME]" + "service_principal_name": "[USERNAME]" } ] }, @@ -139,15 +139,6 @@ Deployment complete! "object_id": { "action": "update" } - }, - "remote": { - "permissions[1].service_principal_name": { - "action": "skip", - "reason": "server_side_default" - }, - "permissions[1].user_name": { - "action": "update" - } } } } @@ -183,7 +174,7 @@ Deployment complete! }, { "permission_level": "CAN_MANAGE", - "user_name": "[USERNAME]" + "service_principal_name": "[USERNAME]" } ] } diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml index 30fe47d72d..450b33ef30 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml @@ -1,4 +1,4 @@ -Local = true +Local = false Cloud = true RecordRequests = true From 291b9b881ec60f0a4e5fe32feeb2dad1ea42246d Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 7 Nov 2025 17:29:34 +0100 Subject: [PATCH 07/26] use jq to sort' --- .../basic/out.first-plan.direct.json | 39 ++++++ .../basic/out.second-plan.direct.json | 66 +++++++++ .../basic/out.test.toml | 2 +- .../model_serving_endpoints/basic/output.txt | 129 ++---------------- .../model_serving_endpoints/basic/script | 8 +- .../model_serving_endpoints/basic/test.toml | 2 +- 6 files changed, 123 insertions(+), 123 deletions(-) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json new file mode 100644 index 0000000000..f4b0896465 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json @@ -0,0 +1,39 @@ +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "create", + "new_state": { + "config": { + "name": "[ENDPOINT_NAME_1]" + } + } + }, + "resources.model_serving_endpoints.my_endpoint.permissions": { + "depends_on": [ + { + "node": "resources.model_serving_endpoints.my_endpoint", + "label": "${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + ], + "action": "create", + "new_state": { + "config": { + "object_id": "", + "permissions": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + } + ] + }, + "vars": { + "object_id": "/serving-endpoints/${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json new file mode 100644 index 0000000000..89ddb2088f --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json @@ -0,0 +1,66 @@ +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "name": "[ENDPOINT_NAME_2]" + } + }, + "changes": { + "local": { + "name": { + "action": "recreate" + } + } + } + }, + "resources.model_serving_endpoints.my_endpoint.permissions": { + "depends_on": [ + { + "node": "resources.model_serving_endpoints.my_endpoint", + "label": "${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + ], + "action": "update", + "new_state": { + "config": { + "object_id": "", + "permissions": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + } + ] + }, + "vars": { + "object_id": "/serving-endpoints/${resources.model_serving_endpoints.my_endpoint.endpoint_id}" + } + }, + "remote_state": { + "object_id": "/serving-endpoints/[ENDPOINT_ID_1]", + "permissions": [ + { + "permission_level": "CAN_VIEW", + "user_name": "[TEST_USER_EMAIL]" + }, + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + } + ] + }, + "changes": { + "local": { + "object_id": { + "action": "update" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml index 1ae6b5ffce..19b2c349a3 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml @@ -1,4 +1,4 @@ -Local = false +Local = true Cloud = true [EnvMatrix] diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt index d1723211ff..16a5c3e6fe 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt @@ -1,44 +1,5 @@ >>> [CLI] bundle debug plan -{ - "plan": { - "resources.model_serving_endpoints.my_endpoint": { - "action": "create", - "new_state": { - "config": { - "name": "[ENDPOINT_NAME_1]" - } - } - }, - "resources.model_serving_endpoints.my_endpoint.permissions": { - "depends_on": [ - { - "node": "resources.model_serving_endpoints.my_endpoint", - "label": "${resources.model_serving_endpoints.my_endpoint.endpoint_id}" - } - ], - "action": "create", - "new_state": { - "config": { - "object_id": "", - "permissions": [ - { - "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" - }, - { - "permission_level": "CAN_MANAGE", - "service_principal_name": "[USERNAME]" - } - ] - }, - "vars": { - "object_id": "/serving-endpoints/${resources.model_serving_endpoints.my_endpoint.endpoint_id}" - } - } - } - } -} >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... @@ -48,15 +9,13 @@ Deployment complete! >>> print_requests.py //serving-endpoints { - "method": "POST", - "path": "/api/2.0/serving-endpoints", "body": { "name": "[ENDPOINT_NAME_1]" - } + }, + "method": "POST", + "path": "/api/2.0/serving-endpoints" } { - "method": "PUT", - "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_1]", "body": { "access_control_list": [ { @@ -68,7 +27,9 @@ Deployment complete! "service_principal_name": "[USERNAME]" } ] - } + }, + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_1]" } === Print the GET endpoint details @@ -78,72 +39,6 @@ Deployment complete! } >>> [CLI] bundle debug plan -{ - "plan": { - "resources.model_serving_endpoints.my_endpoint": { - "action": "recreate", - "new_state": { - "config": { - "name": "[ENDPOINT_NAME_2]" - } - }, - "changes": { - "local": { - "name": { - "action": "recreate" - } - } - } - }, - "resources.model_serving_endpoints.my_endpoint.permissions": { - "depends_on": [ - { - "node": "resources.model_serving_endpoints.my_endpoint", - "label": "${resources.model_serving_endpoints.my_endpoint.endpoint_id}" - } - ], - "action": "update", - "new_state": { - "config": { - "object_id": "", - "permissions": [ - { - "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" - }, - { - "permission_level": "CAN_MANAGE", - "service_principal_name": "[USERNAME]" - } - ] - }, - "vars": { - "object_id": "/serving-endpoints/${resources.model_serving_endpoints.my_endpoint.endpoint_id}" - } - }, - "remote_state": { - "object_id": "/serving-endpoints/[ENDPOINT_ID_1]", - "permissions": [ - { - "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" - }, - { - "permission_level": "CAN_MANAGE", - "service_principal_name": "[USERNAME]" - } - ] - }, - "changes": { - "local": { - "object_id": { - "action": "update" - } - } - } - } - } -} >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/acc-[UNIQUE_NAME]/default/files... @@ -157,15 +52,13 @@ Deployment complete! "path": "/api/2.0/serving-endpoints/[ENDPOINT_NAME_1]" } { - "method": "POST", - "path": "/api/2.0/serving-endpoints", "body": { "name": "[ENDPOINT_NAME_2]" - } + }, + "method": "POST", + "path": "/api/2.0/serving-endpoints" } { - "method": "PUT", - "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_2]", "body": { "access_control_list": [ { @@ -177,7 +70,9 @@ Deployment complete! "service_principal_name": "[USERNAME]" } ] - } + }, + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_2]" } === Print the GET endpoint details diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/script b/acceptance/bundle/resources/model_serving_endpoints/basic/script index 3f94168e9e..bd70f09022 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/script +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/script @@ -14,9 +14,9 @@ cleanup() { } trap cleanup EXIT -trace $CLI bundle debug plan +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy -trace print_requests.py //serving-endpoints +trace print_requests.py //serving-endpoints | jq --sort-keys echo "$ENDPOINT_NAME:ENDPOINT_NAME_1" >> ACC_REPLS @@ -30,9 +30,9 @@ echo "$get_output" | jq '{name, creator}' export ENDPOINT_NAME="test-endpoint-$UNIQUE_NAME-2" envsubst < databricks.yml.tmpl > databricks.yml -trace $CLI bundle debug plan +trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy -trace print_requests.py //serving-endpoints +trace print_requests.py //serving-endpoints | jq --sort-keys echo "$ENDPOINT_NAME:ENDPOINT_NAME_2" >> ACC_REPLS diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml index 450b33ef30..30fe47d72d 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml @@ -1,4 +1,4 @@ -Local = false +Local = true Cloud = true RecordRequests = true From 55717cd13507fcfc61caf1733f72206297a71df9 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 7 Nov 2025 17:30:06 +0100 Subject: [PATCH 08/26] 0 --- .../model_serving_endpoints/running-endpoint/test.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml index 8afe51c16f..b3c25f76e7 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/test.toml @@ -1,6 +1,7 @@ -# Timeout = '5m' +Timeout = '30m' Cloud = true Local = true +CloudSlow = true [[Server]] Pattern = "GET /api/2.1/unity-catalog/models/system.ai.llama_v3_2_1b_instruct/versions" From 6a329b41b7dc876377d3c6bc90cc6b055b6d14c8 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 7 Nov 2025 17:34:06 +0100 Subject: [PATCH 09/26] update --- acceptance/bundle/refschema/out.fields.txt | 469 ++++++++++++++++++ .../catalog-name/out.first-plan.direct.json | 0 .../out.second-plan.direct-exp.json | 39 -- ...ct-exp.json => out.first-plan.direct.json} | 0 ...t-exp.json => out.second-plan.direct.json} | 0 ...ct-exp.json => out.first-plan.direct.json} | 0 ...t-exp.json => out.second-plan.direct.json} | 0 .../out.first-plan.direct.json} | 0 ...t-exp.json => out.second-plan.direct.json} | 0 .../out.first-plan.direct-exp.json | 32 -- .../out.first-plan.direct.json} | 0 ...t-exp.json => out.second-plan.direct.json} | 0 ...lan.direct-exp.txt => out.plan.direct.txt} | 0 .../running-endpoint/out.test.toml | 1 + ...ct-exp.json => out.first-plan.direct.json} | 0 ...t-exp.json => out.second-plan.direct.json} | 43 +- ...ct-exp.json => out.first-plan.direct.json} | 0 ...t-exp.json => out.second-plan.direct.json} | 41 +- ...ct-exp.json => out.first-plan.direct.json} | 0 ...t-exp.json => out.second-plan.direct.json} | 57 ++- 20 files changed, 545 insertions(+), 137 deletions(-) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json rename acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/{out.first-plan.direct-exp.json => out.first-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/{out.second-plan.direct-exp.json => out.second-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/{out.first-plan.direct-exp.json => out.first-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/{out.second-plan.direct-exp.json => out.second-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/recreate/{catalog-name/out.first-plan.direct-exp.json => schema-name/out.first-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/{out.second-plan.direct-exp.json => out.second-plan.direct.json} (100%) delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json rename acceptance/bundle/resources/model_serving_endpoints/recreate/{schema-name/out.first-plan.direct-exp.json => table-prefix/out.first-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/{out.second-plan.direct-exp.json => out.second-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/running-endpoint/{out.plan.direct-exp.txt => out.plan.direct.txt} (100%) rename acceptance/bundle/resources/model_serving_endpoints/update/description/{out.first-plan.direct-exp.json => out.first-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/update/description/{out.second-plan.direct-exp.json => out.second-plan.direct.json} (55%) rename acceptance/bundle/resources/model_serving_endpoints/update/served-entities/{out.first-plan.direct-exp.json => out.first-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/update/served-entities/{out.second-plan.direct-exp.json => out.second-plan.direct.json} (56%) rename acceptance/bundle/resources/model_serving_endpoints/update/tags/{out.first-plan.direct-exp.json => out.first-plan.direct.json} (100%) rename acceptance/bundle/resources/model_serving_endpoints/update/tags/{out.second-plan.direct-exp.json => out.second-plan.direct.json} (57%) diff --git a/acceptance/bundle/refschema/out.fields.txt b/acceptance/bundle/refschema/out.fields.txt index fd65a5a59f..bebec8c374 100644 --- a/acceptance/bundle/refschema/out.fields.txt +++ b/acceptance/bundle/refschema/out.fields.txt @@ -2252,6 +2252,475 @@ resources.jobs.*.permissions.permissions[*].group_name string ALL resources.jobs.*.permissions.permissions[*].permission_level iam.PermissionLevel ALL resources.jobs.*.permissions.permissions[*].service_principal_name string ALL resources.jobs.*.permissions.permissions[*].user_name string ALL +resources.model_serving_endpoints.*.ai_gateway *serving.AiGatewayConfig INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.fallback_config *serving.FallbackConfig INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.fallback_config.enabled bool INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails *serving.AiGatewayGuardrails INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input *serving.AiGatewayGuardrailParameters INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.invalid_keywords []string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.invalid_keywords[*] string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.pii *serving.AiGatewayGuardrailPiiBehavior INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.pii.behavior serving.AiGatewayGuardrailPiiBehaviorBehavior INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.safety bool INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.valid_topics []string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.valid_topics[*] string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output *serving.AiGatewayGuardrailParameters INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.invalid_keywords []string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.invalid_keywords[*] string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.pii *serving.AiGatewayGuardrailPiiBehavior INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.pii.behavior serving.AiGatewayGuardrailPiiBehaviorBehavior INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.safety bool INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.valid_topics []string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.valid_topics[*] string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.inference_table_config *serving.AiGatewayInferenceTableConfig INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.catalog_name string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.enabled bool INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.schema_name string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.table_name_prefix string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.rate_limits []serving.AiGatewayRateLimit INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*] serving.AiGatewayRateLimit INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].calls int64 INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].key serving.AiGatewayRateLimitKey INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].principal string INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].renewal_period serving.AiGatewayRateLimitRenewalPeriod INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].tokens int64 INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.usage_tracking_config *serving.AiGatewayUsageTrackingConfig INPUT STATE +resources.model_serving_endpoints.*.ai_gateway.usage_tracking_config.enabled bool INPUT STATE +resources.model_serving_endpoints.*.budget_policy_id string INPUT STATE +resources.model_serving_endpoints.*.config *serving.EndpointCoreConfigInput INPUT STATE +resources.model_serving_endpoints.*.config.auto_capture_config *serving.AutoCaptureConfigInput INPUT STATE +resources.model_serving_endpoints.*.config.auto_capture_config.catalog_name string INPUT STATE +resources.model_serving_endpoints.*.config.auto_capture_config.enabled bool INPUT STATE +resources.model_serving_endpoints.*.config.auto_capture_config.schema_name string INPUT STATE +resources.model_serving_endpoints.*.config.auto_capture_config.table_name_prefix string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities []serving.ServedEntityInput INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*] serving.ServedEntityInput INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].entity_name string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].entity_version string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].environment_vars map[string]string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].environment_vars.* string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model *serving.ExternalModel INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.ai21labs_config *serving.Ai21LabsConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config *serving.AmazonBedrockConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_region string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.bedrock_provider serving.AmazonBedrockConfigBedrockProvider INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.instance_profile_arn string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.anthropic_config *serving.AnthropicConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.anthropic_config.anthropic_api_key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.anthropic_config.anthropic_api_key_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config *serving.CohereConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config.cohere_api_base string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config.cohere_api_key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config.cohere_api_key_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config *serving.CustomProviderConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth *serving.ApiKeyAuth INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth.key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth.value string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth.value_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth *serving.BearerTokenAuth INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.custom_provider_url string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config *serving.DatabricksModelServingConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config.databricks_workspace_url string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config *serving.GoogleCloudVertexAiConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.project_id string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.region string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.name string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config *serving.OpenAiConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_client_id string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_tenant_id string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_base string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_key_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_type string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_version string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_deployment_name string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_organization string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.palm_config *serving.PaLmConfig INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.palm_config.palm_api_key string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.palm_config.palm_api_key_plaintext string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.provider serving.ExternalModelProvider INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].external_model.task string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].instance_profile_arn string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].max_provisioned_concurrency int INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].max_provisioned_throughput int INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].min_provisioned_concurrency int INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].min_provisioned_throughput int INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].name string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].provisioned_model_units int64 INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].scale_to_zero_enabled bool INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].workload_size string INPUT STATE +resources.model_serving_endpoints.*.config.served_entities[*].workload_type serving.ServingModelWorkloadType INPUT STATE +resources.model_serving_endpoints.*.config.served_models []serving.ServedModelInput INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*] serving.ServedModelInput INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].environment_vars map[string]string INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].environment_vars.* string INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].instance_profile_arn string INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].max_provisioned_concurrency int INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].max_provisioned_throughput int INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].min_provisioned_concurrency int INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].min_provisioned_throughput int INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].model_name string INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].model_version string INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].name string INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].provisioned_model_units int64 INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].scale_to_zero_enabled bool INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].workload_size string INPUT STATE +resources.model_serving_endpoints.*.config.served_models[*].workload_type serving.ServedModelInputWorkloadType INPUT STATE +resources.model_serving_endpoints.*.config.traffic_config *serving.TrafficConfig INPUT STATE +resources.model_serving_endpoints.*.config.traffic_config.routes []serving.Route INPUT STATE +resources.model_serving_endpoints.*.config.traffic_config.routes[*] serving.Route INPUT STATE +resources.model_serving_endpoints.*.config.traffic_config.routes[*].served_entity_name string INPUT STATE +resources.model_serving_endpoints.*.config.traffic_config.routes[*].served_model_name string INPUT STATE +resources.model_serving_endpoints.*.config.traffic_config.routes[*].traffic_percentage int INPUT STATE +resources.model_serving_endpoints.*.description string INPUT STATE +resources.model_serving_endpoints.*.email_notifications *serving.EmailNotifications INPUT STATE +resources.model_serving_endpoints.*.email_notifications.on_update_failure []string INPUT STATE +resources.model_serving_endpoints.*.email_notifications.on_update_failure[*] string INPUT STATE +resources.model_serving_endpoints.*.email_notifications.on_update_success []string INPUT STATE +resources.model_serving_endpoints.*.email_notifications.on_update_success[*] string INPUT STATE +resources.model_serving_endpoints.*.endpoint_details *serving.ServingEndpointDetailed REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway *serving.AiGatewayConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.fallback_config *serving.FallbackConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.fallback_config.enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails *serving.AiGatewayGuardrails REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input *serving.AiGatewayGuardrailParameters REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input.invalid_keywords []string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input.invalid_keywords[*] string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input.pii *serving.AiGatewayGuardrailPiiBehavior REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input.pii.behavior serving.AiGatewayGuardrailPiiBehaviorBehavior REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input.safety bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input.valid_topics []string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.input.valid_topics[*] string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output *serving.AiGatewayGuardrailParameters REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output.invalid_keywords []string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output.invalid_keywords[*] string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output.pii *serving.AiGatewayGuardrailPiiBehavior REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output.pii.behavior serving.AiGatewayGuardrailPiiBehaviorBehavior REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output.safety bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output.valid_topics []string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.guardrails.output.valid_topics[*] string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.inference_table_config *serving.AiGatewayInferenceTableConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.inference_table_config.catalog_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.inference_table_config.enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.inference_table_config.schema_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.inference_table_config.table_name_prefix string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.rate_limits []serving.AiGatewayRateLimit REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.rate_limits[*] serving.AiGatewayRateLimit REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.rate_limits[*].calls int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.rate_limits[*].key serving.AiGatewayRateLimitKey REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.rate_limits[*].principal string REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.rate_limits[*].renewal_period serving.AiGatewayRateLimitRenewalPeriod REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.rate_limits[*].tokens int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.usage_tracking_config *serving.AiGatewayUsageTrackingConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.ai_gateway.usage_tracking_config.enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.budget_policy_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config *serving.EndpointCoreConfigOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config *serving.AutoCaptureConfigOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.catalog_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.schema_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.state *serving.AutoCaptureState REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.state.payload_table *serving.PayloadTable REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.state.payload_table.name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.state.payload_table.status string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.state.payload_table.status_message string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.auto_capture_config.table_name_prefix string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.config_version int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities []serving.ServedEntityOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*] serving.ServedEntityOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].creation_timestamp int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].creator string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].entity_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].entity_version string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].environment_vars map[string]string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].environment_vars.* string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model *serving.ExternalModel REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.ai21labs_config *serving.Ai21LabsConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config *serving.AmazonBedrockConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config.aws_region string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config.bedrock_provider serving.AmazonBedrockConfigBedrockProvider REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.amazon_bedrock_config.instance_profile_arn string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.anthropic_config *serving.AnthropicConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.anthropic_config.anthropic_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.anthropic_config.anthropic_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.cohere_config *serving.CohereConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.cohere_config.cohere_api_base string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.cohere_config.cohere_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.cohere_config.cohere_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config *serving.CustomProviderConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.api_key_auth *serving.ApiKeyAuth REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.api_key_auth.key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.api_key_auth.value string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.api_key_auth.value_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth *serving.BearerTokenAuth REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.custom_provider_config.custom_provider_url string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.databricks_model_serving_config *serving.DatabricksModelServingConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.databricks_model_serving_config.databricks_workspace_url string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.google_cloud_vertex_ai_config *serving.GoogleCloudVertexAiConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.google_cloud_vertex_ai_config.project_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.google_cloud_vertex_ai_config.region string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config *serving.OpenAiConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.microsoft_entra_client_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.microsoft_entra_tenant_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.openai_api_base string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.openai_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.openai_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.openai_api_type string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.openai_api_version string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.openai_deployment_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.openai_config.openai_organization string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.palm_config *serving.PaLmConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.palm_config.palm_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.palm_config.palm_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.provider serving.ExternalModelProvider REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].external_model.task string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].foundation_model *serving.FoundationModel REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].foundation_model.description string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].foundation_model.display_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].foundation_model.docs string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].foundation_model.name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].instance_profile_arn string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].max_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].max_provisioned_throughput int REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].min_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].min_provisioned_throughput int REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].provisioned_model_units int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].scale_to_zero_enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].state *serving.ServedModelState REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].state.deployment serving.ServedModelStateDeployment REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].state.deployment_state_message string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].workload_size string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_entities[*].workload_type serving.ServingModelWorkloadType REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models []serving.ServedModelOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*] serving.ServedModelOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].creation_timestamp int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].creator string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].environment_vars map[string]string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].environment_vars.* string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].instance_profile_arn string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].max_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].min_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].model_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].model_version string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].provisioned_model_units int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].scale_to_zero_enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].state *serving.ServedModelState REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].state.deployment serving.ServedModelStateDeployment REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].state.deployment_state_message string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].workload_size string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.served_models[*].workload_type serving.ServingModelWorkloadType REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.traffic_config *serving.TrafficConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.traffic_config.routes []serving.Route REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.traffic_config.routes[*] serving.Route REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.traffic_config.routes[*].served_entity_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.traffic_config.routes[*].served_model_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.config.traffic_config.routes[*].traffic_percentage int REMOTE +resources.model_serving_endpoints.*.endpoint_details.creation_timestamp int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.creator string REMOTE +resources.model_serving_endpoints.*.endpoint_details.data_plane_info *serving.ModelDataPlaneInfo REMOTE +resources.model_serving_endpoints.*.endpoint_details.data_plane_info.query_info *serving.DataPlaneInfo REMOTE +resources.model_serving_endpoints.*.endpoint_details.data_plane_info.query_info.authorization_details string REMOTE +resources.model_serving_endpoints.*.endpoint_details.data_plane_info.query_info.endpoint_url string REMOTE +resources.model_serving_endpoints.*.endpoint_details.description string REMOTE +resources.model_serving_endpoints.*.endpoint_details.email_notifications *serving.EmailNotifications REMOTE +resources.model_serving_endpoints.*.endpoint_details.email_notifications.on_update_failure []string REMOTE +resources.model_serving_endpoints.*.endpoint_details.email_notifications.on_update_failure[*] string REMOTE +resources.model_serving_endpoints.*.endpoint_details.email_notifications.on_update_success []string REMOTE +resources.model_serving_endpoints.*.endpoint_details.email_notifications.on_update_success[*] string REMOTE +resources.model_serving_endpoints.*.endpoint_details.endpoint_url string REMOTE +resources.model_serving_endpoints.*.endpoint_details.id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.last_updated_timestamp int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config *serving.EndpointPendingConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config *serving.AutoCaptureConfigOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.catalog_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.schema_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.state *serving.AutoCaptureState REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.state.payload_table *serving.PayloadTable REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.state.payload_table.name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.state.payload_table.status string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.state.payload_table.status_message string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.auto_capture_config.table_name_prefix string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.config_version int REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities []serving.ServedEntityOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*] serving.ServedEntityOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].creation_timestamp int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].creator string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].entity_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].entity_version string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].environment_vars map[string]string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].environment_vars.* string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model *serving.ExternalModel REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.ai21labs_config *serving.Ai21LabsConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config *serving.AmazonBedrockConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config.aws_region string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config.bedrock_provider serving.AmazonBedrockConfigBedrockProvider REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.amazon_bedrock_config.instance_profile_arn string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.anthropic_config *serving.AnthropicConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.anthropic_config.anthropic_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.anthropic_config.anthropic_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.cohere_config *serving.CohereConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.cohere_config.cohere_api_base string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.cohere_config.cohere_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.cohere_config.cohere_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config *serving.CustomProviderConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.api_key_auth *serving.ApiKeyAuth REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.api_key_auth.key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.api_key_auth.value string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.api_key_auth.value_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.bearer_token_auth *serving.BearerTokenAuth REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.custom_provider_config.custom_provider_url string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.databricks_model_serving_config *serving.DatabricksModelServingConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.databricks_model_serving_config.databricks_workspace_url string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.google_cloud_vertex_ai_config *serving.GoogleCloudVertexAiConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.google_cloud_vertex_ai_config.project_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.google_cloud_vertex_ai_config.region string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config *serving.OpenAiConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.microsoft_entra_client_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.microsoft_entra_tenant_id string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.openai_api_base string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.openai_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.openai_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.openai_api_type string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.openai_api_version string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.openai_deployment_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.openai_config.openai_organization string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.palm_config *serving.PaLmConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.palm_config.palm_api_key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.palm_config.palm_api_key_plaintext string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.provider serving.ExternalModelProvider REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].external_model.task string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].foundation_model *serving.FoundationModel REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].foundation_model.description string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].foundation_model.display_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].foundation_model.docs string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].foundation_model.name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].instance_profile_arn string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].max_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].max_provisioned_throughput int REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].min_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].min_provisioned_throughput int REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].provisioned_model_units int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].scale_to_zero_enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].state *serving.ServedModelState REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].state.deployment serving.ServedModelStateDeployment REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].state.deployment_state_message string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].workload_size string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_entities[*].workload_type serving.ServingModelWorkloadType REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models []serving.ServedModelOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*] serving.ServedModelOutput REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].creation_timestamp int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].creator string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].environment_vars map[string]string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].environment_vars.* string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].instance_profile_arn string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].max_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].min_provisioned_concurrency int REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].model_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].model_version string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].provisioned_model_units int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].scale_to_zero_enabled bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].state *serving.ServedModelState REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].state.deployment serving.ServedModelStateDeployment REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].state.deployment_state_message string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].workload_size string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.served_models[*].workload_type serving.ServingModelWorkloadType REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.start_time int64 REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.traffic_config *serving.TrafficConfig REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.traffic_config.routes []serving.Route REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.traffic_config.routes[*] serving.Route REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.traffic_config.routes[*].served_entity_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.traffic_config.routes[*].served_model_name string REMOTE +resources.model_serving_endpoints.*.endpoint_details.pending_config.traffic_config.routes[*].traffic_percentage int REMOTE +resources.model_serving_endpoints.*.endpoint_details.permission_level serving.ServingEndpointDetailedPermissionLevel REMOTE +resources.model_serving_endpoints.*.endpoint_details.route_optimized bool REMOTE +resources.model_serving_endpoints.*.endpoint_details.state *serving.EndpointState REMOTE +resources.model_serving_endpoints.*.endpoint_details.state.config_update serving.EndpointStateConfigUpdate REMOTE +resources.model_serving_endpoints.*.endpoint_details.state.ready serving.EndpointStateReady REMOTE +resources.model_serving_endpoints.*.endpoint_details.tags []serving.EndpointTag REMOTE +resources.model_serving_endpoints.*.endpoint_details.tags[*] serving.EndpointTag REMOTE +resources.model_serving_endpoints.*.endpoint_details.tags[*].key string REMOTE +resources.model_serving_endpoints.*.endpoint_details.tags[*].value string REMOTE +resources.model_serving_endpoints.*.endpoint_details.task string REMOTE +resources.model_serving_endpoints.*.endpoint_id string REMOTE +resources.model_serving_endpoints.*.id string INPUT +resources.model_serving_endpoints.*.lifecycle resources.Lifecycle INPUT +resources.model_serving_endpoints.*.lifecycle.prevent_destroy bool INPUT +resources.model_serving_endpoints.*.modified_status string INPUT +resources.model_serving_endpoints.*.name string INPUT STATE +resources.model_serving_endpoints.*.permissions []resources.ModelServingEndpointPermission INPUT +resources.model_serving_endpoints.*.permissions[*] resources.ModelServingEndpointPermission INPUT +resources.model_serving_endpoints.*.permissions[*].group_name string INPUT +resources.model_serving_endpoints.*.permissions[*].level resources.ModelServingEndpointPermissionLevel INPUT +resources.model_serving_endpoints.*.permissions[*].service_principal_name string INPUT +resources.model_serving_endpoints.*.permissions[*].user_name string INPUT +resources.model_serving_endpoints.*.rate_limits []serving.RateLimit INPUT STATE +resources.model_serving_endpoints.*.rate_limits[*] serving.RateLimit INPUT STATE +resources.model_serving_endpoints.*.rate_limits[*].calls int64 INPUT STATE +resources.model_serving_endpoints.*.rate_limits[*].key serving.RateLimitKey INPUT STATE +resources.model_serving_endpoints.*.rate_limits[*].renewal_period serving.RateLimitRenewalPeriod INPUT STATE +resources.model_serving_endpoints.*.route_optimized bool INPUT STATE +resources.model_serving_endpoints.*.tags []serving.EndpointTag INPUT STATE +resources.model_serving_endpoints.*.tags[*] serving.EndpointTag INPUT STATE +resources.model_serving_endpoints.*.tags[*].key string INPUT STATE +resources.model_serving_endpoints.*.tags[*].value string INPUT STATE +resources.model_serving_endpoints.*.url string INPUT +resources.model_serving_endpoints.*.permissions.object_id string ALL +resources.model_serving_endpoints.*.permissions.permissions []iam.AccessControlRequest ALL +resources.model_serving_endpoints.*.permissions.permissions[*] iam.AccessControlRequest ALL +resources.model_serving_endpoints.*.permissions.permissions[*].group_name string ALL +resources.model_serving_endpoints.*.permissions.permissions[*].permission_level iam.PermissionLevel ALL +resources.model_serving_endpoints.*.permissions.permissions[*].service_principal_name string ALL +resources.model_serving_endpoints.*.permissions.permissions[*].user_name string ALL resources.models.*.creation_timestamp int64 REMOTE resources.models.*.description string ALL resources.models.*.id string INPUT REMOTE diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct.json new file mode 100644 index 0000000000..e69de29bb2 diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json deleted file mode 100644 index 156261de42..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct-exp.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "recreate", - "new_state": { - "config": { - "config": { - "auto_capture_config": { - "catalog_name": "other_catalog", - "schema_name": "default", - "table_name_prefix": "my_table" - }, - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ORIGINAL_ENDPOINT_ID]" - } - }, - "changes": { - "local": { - "config.auto_capture_config.catalog_name": { - "action": "recreate" - } - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.first-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json deleted file mode 100644 index 11afeef35c..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct-exp.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "create", - "new_state": { - "config": { - "config": { - "auto_capture_config": { - "catalog_name": "main", - "schema_name": "default", - "table_name_prefix": "my_table" - }, - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ORIGINAL_ENDPOINT_ID]" - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.first-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.first-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct.txt similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct-exp.txt rename to acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.plan.direct.txt diff --git a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml index 01ed6822af..5366fbb1a4 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/running-endpoint/out.test.toml @@ -1,5 +1,6 @@ Local = true Cloud = true +CloudSlow = true [EnvMatrix] DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json similarity index 55% rename from acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json index b65bf69e7a..019cf28c1d 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct-exp.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json @@ -24,28 +24,31 @@ } }, "remote_state": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" + "endpoint_details": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] + "name": "prod" + } + ] + }, + "creator": "[USERNAME]", + "description": "Initial description", + "id": "[UUID]", + "name": "[ENDPOINT_ID]", + "state": { + "config_update": "NOT_UPDATING" + } }, - "creator": "[USERNAME]", - "description": "Initial description", - "id": "[UUID]", - "name": "[ENDPOINT_ID]", - "state": { - "config_update": "NOT_UPDATING" - } + "endpoint_id": "[UUID]" }, "changes": { "local": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct.json similarity index 56% rename from acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct.json index b2c75feacb..25a39cf360 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct-exp.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct.json @@ -23,27 +23,30 @@ } }, "remote_state": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" + "endpoint_details": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] + "name": "prod" + } + ] + }, + "creator": "[USERNAME]", + "id": "[UUID]", + "name": "[ENDPOINT_ID]", + "state": { + "config_update": "NOT_UPDATING" + } }, - "creator": "[USERNAME]", - "id": "[UUID]", - "name": "[ENDPOINT_ID]", - "state": { - "config_update": "NOT_UPDATING" - } + "endpoint_id": "[UUID]" }, "changes": { "local": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct.json similarity index 100% rename from acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct-exp.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct.json similarity index 57% rename from acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct-exp.json rename to acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct.json index 0a57ab0433..adaf5bccb5 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct-exp.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct.json @@ -33,37 +33,40 @@ } }, "remote_state": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" + "endpoint_details": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" + "name": "prod" + } + ] + }, + "creator": "[USERNAME]", + "id": "[UUID]", + "name": "[ENDPOINT_ID]", + "state": { + "config_update": "NOT_UPDATING" + }, + "tags": [ + { + "key": "env", + "value": "test" + }, + { + "key": "team", + "value": "ml" } ] }, - "creator": "[USERNAME]", - "id": "[UUID]", - "name": "[ENDPOINT_ID]", - "state": { - "config_update": "NOT_UPDATING" - }, - "tags": [ - { - "key": "env", - "value": "test" - }, - { - "key": "team", - "value": "ml" - } - ] + "endpoint_id": "[UUID]" }, "changes": { "local": { From 2623ef0f00c18776dc4ea0f91ee36f13fceb08ea Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 7 Nov 2025 18:23:42 +0100 Subject: [PATCH 10/26] make test work: --- .../basic/databricks.yml.tmpl | 4 +- .../basic/out.first-plan.direct.json | 2 +- .../basic/out.first-plan.terraform.json | 7 +++ .../basic/out.first-requests.direct.json | 23 +++++++++ .../basic/out.first-requests.terraform.json | 23 +++++++++ .../basic/out.second-plan.direct.json | 4 +- .../basic/out.second-plan.terraform.json | 7 +++ .../basic/out.second-requests.direct.json | 27 ++++++++++ .../basic/out.second-requests.terraform.json | 39 +++++++++++++++ .../basic/out.test.toml | 4 +- .../model_serving_endpoints/basic/output.txt | 50 ------------------- .../model_serving_endpoints/basic/script | 10 +--- .../model_serving_endpoints/basic/test.toml | 4 +- 13 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.direct.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.direct.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.terraform.json diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl index 854624dcc0..52269097f4 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/databricks.yml.tmpl @@ -8,6 +8,4 @@ resources: permissions: - level: CAN_VIEW - user_name: $TEST_USER_EMAIL - - level: CAN_MANAGE - service_principal_name: $CURRENT_USER_NAME + user_name: deco-test-user@databricks.com diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json index f4b0896465..8dbb050838 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.direct.json @@ -22,7 +22,7 @@ "permissions": [ { "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" + "user_name": "deco-test-user@databricks.com" }, { "permission_level": "CAN_MANAGE", diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.terraform.json new file mode 100644 index 0000000000..1d92fcbea4 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "create" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.direct.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.direct.json new file mode 100644 index 0000000000..7231a1d7fc --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.direct.json @@ -0,0 +1,23 @@ +{ + "body": { + "name": "[ENDPOINT_NAME_1]" + }, + "method": "POST", + "path": "/api/2.0/serving-endpoints" +} +{ + "body": { + "access_control_list": [ + { + "permission_level": "CAN_VIEW", + "user_name": "deco-test-user@databricks.com" + }, + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + } + ] + }, + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_1]" +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.terraform.json new file mode 100644 index 0000000000..70a6d2b840 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.first-requests.terraform.json @@ -0,0 +1,23 @@ +{ + "body": { + "name": "[ENDPOINT_NAME_1]" + }, + "method": "POST", + "path": "/api/2.0/serving-endpoints" +} +{ + "body": { + "access_control_list": [ + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + }, + { + "permission_level": "CAN_VIEW", + "user_name": "deco-test-user@databricks.com" + } + ] + }, + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_1]" +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json index 89ddb2088f..768c8eaac2 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.direct.json @@ -29,7 +29,7 @@ "permissions": [ { "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" + "user_name": "deco-test-user@databricks.com" }, { "permission_level": "CAN_MANAGE", @@ -46,7 +46,7 @@ "permissions": [ { "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" + "user_name": "deco-test-user@databricks.com" }, { "permission_level": "CAN_MANAGE", diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.terraform.json new file mode 100644 index 0000000000..0e6bc43fd9 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-plan.terraform.json @@ -0,0 +1,7 @@ +{ + "plan": { + "resources.model_serving_endpoints.my_endpoint": { + "action": "recreate" + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.direct.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.direct.json new file mode 100644 index 0000000000..6826868502 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.direct.json @@ -0,0 +1,27 @@ +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_NAME_1]" +} +{ + "body": { + "name": "[ENDPOINT_NAME_2]" + }, + "method": "POST", + "path": "/api/2.0/serving-endpoints" +} +{ + "body": { + "access_control_list": [ + { + "permission_level": "CAN_VIEW", + "user_name": "deco-test-user@databricks.com" + }, + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + } + ] + }, + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_2]" +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.terraform.json new file mode 100644 index 0000000000..11bb519e1c --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.second-requests.terraform.json @@ -0,0 +1,39 @@ +{ + "body": { + "access_control_list": [ + { + "permission_level": "CAN_MANAGE", + "user_name": "[USERNAME]" + } + ] + }, + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_1]" +} +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_NAME_1]" +} +{ + "body": { + "name": "[ENDPOINT_NAME_2]" + }, + "method": "POST", + "path": "/api/2.0/serving-endpoints" +} +{ + "body": { + "access_control_list": [ + { + "permission_level": "CAN_MANAGE", + "service_principal_name": "[USERNAME]" + }, + { + "permission_level": "CAN_VIEW", + "user_name": "deco-test-user@databricks.com" + } + ] + }, + "method": "PUT", + "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_2]" +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml index 19b2c349a3..f474b1b917 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/out.test.toml @@ -1,5 +1,5 @@ -Local = true +Local = false Cloud = true [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["direct"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt index 16a5c3e6fe..b5400e5285 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/output.txt @@ -8,29 +8,6 @@ Updating deployment state... Deployment complete! >>> print_requests.py //serving-endpoints -{ - "body": { - "name": "[ENDPOINT_NAME_1]" - }, - "method": "POST", - "path": "/api/2.0/serving-endpoints" -} -{ - "body": { - "access_control_list": [ - { - "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" - }, - { - "permission_level": "CAN_MANAGE", - "service_principal_name": "[USERNAME]" - } - ] - }, - "method": "PUT", - "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_1]" -} === Print the GET endpoint details { @@ -47,33 +24,6 @@ Updating deployment state... Deployment complete! >>> print_requests.py //serving-endpoints -{ - "method": "DELETE", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_NAME_1]" -} -{ - "body": { - "name": "[ENDPOINT_NAME_2]" - }, - "method": "POST", - "path": "/api/2.0/serving-endpoints" -} -{ - "body": { - "access_control_list": [ - { - "permission_level": "CAN_VIEW", - "user_name": "[TEST_USER_EMAIL]" - }, - { - "permission_level": "CAN_MANAGE", - "service_principal_name": "[USERNAME]" - } - ] - }, - "method": "PUT", - "path": "/api/2.0/permissions/serving-endpoints/[ENDPOINT_ID_2]" -} === Print the GET endpoint details { diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/script b/acceptance/bundle/resources/model_serving_endpoints/basic/script index bd70f09022..5201fbeee2 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/script +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/script @@ -1,11 +1,5 @@ export ENDPOINT_NAME="test-endpoint-$UNIQUE_NAME-1" -if [ -z "$CLOUD_ENV" ]; then - export TEST_USER_EMAIL="viewer@databricks.com" -fi - -echo "$TEST_USER_EMAIL:TEST_USER_EMAIL" >> ACC_REPLS - envsubst < databricks.yml.tmpl > databricks.yml cleanup() { @@ -16,7 +10,7 @@ trap cleanup EXIT trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy -trace print_requests.py //serving-endpoints | jq --sort-keys +trace print_requests.py //serving-endpoints | jq --sort-keys > out.first-requests.$DATABRICKS_BUNDLE_ENGINE.json echo "$ENDPOINT_NAME:ENDPOINT_NAME_1" >> ACC_REPLS @@ -32,7 +26,7 @@ export ENDPOINT_NAME="test-endpoint-$UNIQUE_NAME-2" envsubst < databricks.yml.tmpl > databricks.yml trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy -trace print_requests.py //serving-endpoints | jq --sort-keys +trace print_requests.py //serving-endpoints | jq --sort-keys > out.second-requests.$DATABRICKS_BUNDLE_ENGINE.json echo "$ENDPOINT_NAME:ENDPOINT_NAME_2" >> ACC_REPLS diff --git a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml index 30fe47d72d..c64c7dfec0 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml +++ b/acceptance/bundle/resources/model_serving_endpoints/basic/test.toml @@ -1,6 +1,6 @@ -Local = true +Local = false Cloud = true RecordRequests = true [EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["direct"] + DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] From 09f12d143274b91df1863f04aa57d36285ce3c7d Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 7 Nov 2025 18:57:26 +0100 Subject: [PATCH 11/26] better update operation --- .../dresources/model_serving_endpoint.go | 123 ++++++++++++++++-- 1 file changed, 115 insertions(+), 8 deletions(-) diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index 9e1f293016..e16aed062d 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -2,6 +2,7 @@ package dresources import ( "context" + "fmt" "time" "github.com/databricks/cli/bundle/config/resources" @@ -9,6 +10,7 @@ import ( "github.com/databricks/cli/libs/utils" "github.com/databricks/databricks-sdk-go" "github.com/databricks/databricks-sdk-go/service/serving" + "golang.org/x/sync/errgroup" ) type ResourceModelServingEndpoint struct { @@ -144,17 +146,122 @@ func (r *ResourceModelServingEndpoint) WaitAfterCreate(ctx context.Context, conf return r.waitForEndpointReady(ctx, config.Name) } -func (r *ResourceModelServingEndpoint) DoUpdate(ctx context.Context, id string, config *serving.CreateServingEndpoint) error { - if config.Config == nil { - return nil +func (r *ResourceModelServingEndpoint) updateAiGateway(ctx context.Context, id string, aiGateway *serving.AiGatewayConfig) error { + if aiGateway == nil { + req := serving.PutAiGatewayRequest{ + Name: id, + } + _, err := r.client.ServingEndpoints.PutAiGateway(ctx, req) + return err + } + + req := serving.PutAiGatewayRequest{ + Name: id, + FallbackConfig: aiGateway.FallbackConfig, + Guardrails: aiGateway.Guardrails, + InferenceTableConfig: aiGateway.InferenceTableConfig, + RateLimits: aiGateway.RateLimits, + UsageTrackingConfig: aiGateway.UsageTrackingConfig, + } + _, err := r.client.ServingEndpoints.PutAiGateway(ctx, req) + return fmt.Errorf("failed to update AI gateway: %w", err) +} + +// TODO: does unsetting config work properly? +func (r *ResourceModelServingEndpoint) updateConfig(ctx context.Context, id string, config *serving.EndpointCoreConfigInput) error { + if config == nil { + // Unset config in resource. + req := serving.EndpointCoreConfigInput{Name: id} + _, err := r.client.ServingEndpoints.UpdateConfig(ctx, req) + return err + } + req := serving.EndpointCoreConfigInput{ + Name: id, + AutoCaptureConfig: config.AutoCaptureConfig, + ServedEntities: config.ServedEntities, + TrafficConfig: config.TrafficConfig, + ServedModels: config.ServedModels, + } + _, err := r.client.ServingEndpoints.UpdateConfig(ctx, req) + return fmt.Errorf("failed to update config: %w", err) +} + +func (r *ResourceModelServingEndpoint) updateNotifications(ctx context.Context, id string, notifications *serving.EmailNotifications) error { + req := serving.UpdateInferenceEndpointNotifications{ + Name: id, + EmailNotifications: notifications, + } + _, err := r.client.ServingEndpoints.UpdateNotifications(ctx, req) + return fmt.Errorf("failed to update notifications: %w", err) +} + +func diffTags(currentTags []serving.EndpointTag, desiredTags []serving.EndpointTag) (addTags []serving.EndpointTag, deleteTags []string) { + addTags = make([]serving.EndpointTag, 0) + + // build maps for easy lookup. + currentTagsMap := make(map[string]string) + desiredTagsMap := make(map[string]string) + for _, tag := range currentTags { + currentTagsMap[tag.Key] = tag.Value + } + for _, tag := range desiredTags { + desiredTagsMap[tag.Key] = tag.Value } - // UpdateConfig expects an EndpointCoreConfigInput with the Name field set - updateConfig := *config.Config - updateConfig.Name = id + // Compute keys to be added. + for key, desiredValue := range desiredTagsMap { + v, ok := currentTagsMap[key] + if !ok { + addTags = append(addTags, serving.EndpointTag{Key: key, Value: desiredValue}) + continue + } + if v != desiredValue { + addTags = append(addTags, serving.EndpointTag{Key: key, Value: desiredValue}) + } + } - _, err := r.client.ServingEndpoints.UpdateConfig(ctx, updateConfig) - return err + // Compute keys to be deleted. + for key := range currentTagsMap { + if _, ok := desiredTagsMap[key]; !ok { + deleteTags = append(deleteTags, key) + } + } + + return addTags, deleteTags +} + +func (r *ResourceModelServingEndpoint) updateTags(ctx context.Context, id string, tags []serving.EndpointTag) error { + endpoint, err := r.client.ServingEndpoints.GetByName(ctx, id) + if err != nil { + return err + } + + addTags, deleteTags := diffTags(endpoint.Tags, tags) + + req := serving.PatchServingEndpointTags{ + Name: id, + AddTags: addTags, + DeleteTags: deleteTags, + } + _, err = r.client.ServingEndpoints.Patch(ctx, req) + return fmt.Errorf("failed to update tags: %w", err) +} + +func (r *ResourceModelServingEndpoint) DoUpdate(ctx context.Context, id string, config *serving.CreateServingEndpoint) error { + errGroup := errgroup.Group{} + errGroup.Go(func() error { + return r.updateAiGateway(ctx, id, config.AiGateway) + }) + errGroup.Go(func() error { + return r.updateConfig(ctx, id, config.Config) + }) + errGroup.Go(func() error { + return r.updateNotifications(ctx, id, config.EmailNotifications) + }) + errGroup.Go(func() error { + return r.updateTags(ctx, id, config.Tags) + }) + return errGroup.Wait() } func (r *ResourceModelServingEndpoint) WaitAfterUpdate(ctx context.Context, config *serving.CreateServingEndpoint) (*RefreshOutput, error) { From d2c3358b7a2986e7b2655aa654d21cb7487813aa Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 7 Nov 2025 18:58:57 +0100 Subject: [PATCH 12/26] - --- bundle/direct/dresources/model_serving_endpoint.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index e16aed062d..f9d907fe4a 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -41,9 +41,6 @@ func autoCaptureConfigOutputToInput(output *serving.AutoCaptureConfigOutput) *se } func servedEntitiesOutputToInput(output []serving.ServedEntityOutput) []serving.ServedEntityInput { - if len(output) == 0 { - return nil - } entities := make([]serving.ServedEntityInput, len(output)) for i, entity := range output { entities[i] = serving.ServedEntityInput{ From 34df3b074378fc9d023a914ac3e4cd1abd2d9a8f Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Fri, 7 Nov 2025 19:25:36 +0100 Subject: [PATCH 13/26] udpate tests --- .../description/out.second-plan.direct.json | 31 +---- .../update/description/output.txt | 29 +++- .../update/tags/output.txt | 88 ++++++++++-- .../dresources/model_serving_endpoint.go | 23 +++- libs/testserver/handlers.go | 12 ++ libs/testserver/serving_endpoints.go | 128 ++++++++++++++++++ 6 files changed, 265 insertions(+), 46 deletions(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json index 019cf28c1d..3694e643f1 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json @@ -1,7 +1,7 @@ { "plan": { "resources.model_serving_endpoints.test_endpoint": { - "action": "update", + "action": "recreate", "new_state": { "config": { "config": { @@ -23,37 +23,10 @@ "name": "[ENDPOINT_ID]" } }, - "remote_state": { - "endpoint_details": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "creator": "[USERNAME]", - "description": "Initial description", - "id": "[UUID]", - "name": "[ENDPOINT_ID]", - "state": { - "config_update": "NOT_UPDATING" - } - }, - "endpoint_id": "[UUID]" - }, "changes": { "local": { "description": { - "action": "update" + "action": "recreate" } } } diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt index 2875cffdd8..a2ba67bafe 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt @@ -44,9 +44,36 @@ Deployment complete! "name": "[ENDPOINT_ID]" } } +{ + "method": "DELETE", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" +} +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "description": "Updated description", + "name": "[ENDPOINT_ID]" + } +} >>> [CLI] serving-endpoints get [ENDPOINT_ID] -"Initial description" +"Updated description" >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt index 687ebd464c..92d89d40f0 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt @@ -26,17 +26,85 @@ Deployment complete! >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... Deploying resources... -Error: terraform apply: exit status 1 - -Error: cannot update model serving: No stub found for pattern: PATCH /api/2.0/serving-endpoints/[ENDPOINT_ID]/tags - - with databricks_model_serving.test_endpoint, - on bundle.tf.json line 42, in resource.databricks_model_serving.test_endpoint: - 42: } - +Updating deployment state... +Deployment complete! +>>> print_requests.py //serving-endpoints +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ENDPOINT_ID]", + "tags": [ + { + "key": "env", + "value": "test" + }, + { + "key": "team", + "value": "ml" + } + ] + } +} +{ + "method": "PUT", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config", + "body": { + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + } +} +{ + "method": "PATCH", + "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/tags", + "body": { + "add_tags": [ + { + "key": "env", + "value": "prod" + } + ] + } +} -Updating deployment state... +>>> [CLI] serving-endpoints get [ENDPOINT_ID] +[ + { + "key": "env", + "value": "prod" + }, + { + "key": "team", + "value": "ml" + } +] >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: @@ -46,5 +114,3 @@ All files and directories at the following location will be deleted: /Workspace/ Deleting files... Destroy complete! - -Exit code: 1 diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index f9d907fe4a..b3e1f0b91d 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -161,7 +161,10 @@ func (r *ResourceModelServingEndpoint) updateAiGateway(ctx context.Context, id s UsageTrackingConfig: aiGateway.UsageTrackingConfig, } _, err := r.client.ServingEndpoints.PutAiGateway(ctx, req) - return fmt.Errorf("failed to update AI gateway: %w", err) + if err != nil { + return fmt.Errorf("failed to update AI gateway: %w", err) + } + return nil } // TODO: does unsetting config work properly? @@ -180,7 +183,10 @@ func (r *ResourceModelServingEndpoint) updateConfig(ctx context.Context, id stri ServedModels: config.ServedModels, } _, err := r.client.ServingEndpoints.UpdateConfig(ctx, req) - return fmt.Errorf("failed to update config: %w", err) + if err != nil { + return fmt.Errorf("failed to update config: %w", err) + } + return nil } func (r *ResourceModelServingEndpoint) updateNotifications(ctx context.Context, id string, notifications *serving.EmailNotifications) error { @@ -189,7 +195,10 @@ func (r *ResourceModelServingEndpoint) updateNotifications(ctx context.Context, EmailNotifications: notifications, } _, err := r.client.ServingEndpoints.UpdateNotifications(ctx, req) - return fmt.Errorf("failed to update notifications: %w", err) + if err != nil { + return fmt.Errorf("failed to update notifications: %w", err) + } + return nil } func diffTags(currentTags []serving.EndpointTag, desiredTags []serving.EndpointTag) (addTags []serving.EndpointTag, deleteTags []string) { @@ -241,7 +250,10 @@ func (r *ResourceModelServingEndpoint) updateTags(ctx context.Context, id string DeleteTags: deleteTags, } _, err = r.client.ServingEndpoints.Patch(ctx, req) - return fmt.Errorf("failed to update tags: %w", err) + if err != nil { + return fmt.Errorf("failed to update tags: %w", err) + } + return nil } func (r *ResourceModelServingEndpoint) DoUpdate(ctx context.Context, id string, config *serving.CreateServingEndpoint) error { @@ -272,7 +284,8 @@ func (r *ResourceModelServingEndpoint) DoDelete(ctx context.Context, id string) func (*ResourceModelServingEndpoint) FieldTriggers(_ bool) map[string]deployplan.ActionType { // TF implementation: https://github.com/databricks/terraform-provider-databricks/blob/6c106e8e7052bb2726148d66309fd460ed444236/mlflow/resource_mlflow_experiment.go#L22 return map[string]deployplan.ActionType{ - "name": deployplan.ActionTypeRecreate, + "name": deployplan.ActionTypeRecreate, + "description": deployplan.ActionTypeRecreate, // description is immutable, can't be updated via API "config.auto_capture_config.catalog_name": deployplan.ActionTypeRecreate, "config.auto_capture_config.schema_name": deployplan.ActionTypeRecreate, "config.auto_capture_config.table_name_prefix": deployplan.ActionTypeRecreate, diff --git a/libs/testserver/handlers.go b/libs/testserver/handlers.go index 0b78a7d8fe..56ff7616d4 100644 --- a/libs/testserver/handlers.go +++ b/libs/testserver/handlers.go @@ -620,6 +620,18 @@ func AddDefaultHandlers(server *Server) { return MapDelete(req.Workspace, req.Workspace.ServingEndpoints, req.Vars["name"]) }) + server.Handle("PUT", "/api/2.0/serving-endpoints/{name}/ai-gateway", func(req Request) any { + return req.Workspace.ServingEndpointPutAiGateway(req, req.Vars["name"]) + }) + + server.Handle("PATCH", "/api/2.0/serving-endpoints/{name}/notifications", func(req Request) any { + return req.Workspace.ServingEndpointUpdateNotifications(req, req.Vars["name"]) + }) + + server.Handle("PATCH", "/api/2.0/serving-endpoints/{name}/tags", func(req Request) any { + return req.Workspace.ServingEndpointPatchTags(req, req.Vars["name"]) + }) + // Generic permissions endpoints server.Handle("GET", "/api/2.0/permissions/{object_type}/{object_id}", func(req Request) any { return req.Workspace.GetPermissions(req) diff --git a/libs/testserver/serving_endpoints.go b/libs/testserver/serving_endpoints.go index 61975a8730..6f2014ff50 100644 --- a/libs/testserver/serving_endpoints.go +++ b/libs/testserver/serving_endpoints.go @@ -3,6 +3,7 @@ package testserver import ( "encoding/json" "fmt" + "sort" "github.com/databricks/databricks-sdk-go/service/serving" ) @@ -210,3 +211,130 @@ func (s *FakeWorkspace) ServingEndpointUpdate(req Request, name string) Response Body: endpoint, } } + +func (s *FakeWorkspace) ServingEndpointPutAiGateway(req Request, name string) Response { + defer s.LockUnlock()() + + var putReq serving.PutAiGatewayRequest + err := json.Unmarshal(req.Body, &putReq) + if err != nil { + return Response{ + Body: fmt.Sprintf("cannot unmarshal request body: %s", err), + StatusCode: 400, + } + } + + endpoint, exists := s.ServingEndpoints[name] + if !exists { + return Response{ + StatusCode: 404, + Body: map[string]string{"error_code": "RESOURCE_DOES_NOT_EXIST", "message": fmt.Sprintf("Serving endpoint with name %s not found", name)}, + } + } + + // Update AI gateway config + if putReq.FallbackConfig != nil || putReq.Guardrails != nil || putReq.InferenceTableConfig != nil || putReq.RateLimits != nil || putReq.UsageTrackingConfig != nil { + endpoint.AiGateway = &serving.AiGatewayConfig{ + FallbackConfig: putReq.FallbackConfig, + Guardrails: putReq.Guardrails, + InferenceTableConfig: putReq.InferenceTableConfig, + RateLimits: putReq.RateLimits, + UsageTrackingConfig: putReq.UsageTrackingConfig, + } + } else { + // Unset AI gateway if no fields provided + endpoint.AiGateway = nil + } + + s.ServingEndpoints[name] = endpoint + + return Response{ + Body: endpoint.AiGateway, + } +} + +func (s *FakeWorkspace) ServingEndpointUpdateNotifications(req Request, name string) Response { + defer s.LockUnlock()() + + var updateReq serving.UpdateInferenceEndpointNotifications + err := json.Unmarshal(req.Body, &updateReq) + if err != nil { + return Response{ + Body: fmt.Sprintf("cannot unmarshal request body: %s", err), + StatusCode: 400, + } + } + + endpoint, exists := s.ServingEndpoints[name] + if !exists { + return Response{ + StatusCode: 404, + Body: map[string]string{"error_code": "RESOURCE_DOES_NOT_EXIST", "message": fmt.Sprintf("Serving endpoint with name %s not found", name)}, + } + } + + endpoint.EmailNotifications = updateReq.EmailNotifications + s.ServingEndpoints[name] = endpoint + + return Response{ + Body: endpoint, + } +} + +func (s *FakeWorkspace) ServingEndpointPatchTags(req Request, name string) Response { + defer s.LockUnlock()() + + var patchReq serving.PatchServingEndpointTags + err := json.Unmarshal(req.Body, &patchReq) + if err != nil { + return Response{ + Body: fmt.Sprintf("cannot unmarshal request body: %s", err), + StatusCode: 400, + } + } + + endpoint, exists := s.ServingEndpoints[name] + if !exists { + return Response{ + StatusCode: 404, + Body: map[string]string{"error_code": "RESOURCE_DOES_NOT_EXIST", "message": fmt.Sprintf("Serving endpoint with name %s not found", name)}, + } + } + + // Build map of current tags + tagMap := make(map[string]string) + for _, tag := range endpoint.Tags { + tagMap[tag.Key] = tag.Value + } + + // Add or update tags + for _, tag := range patchReq.AddTags { + tagMap[tag.Key] = tag.Value + } + + // Delete tags + for _, key := range patchReq.DeleteTags { + delete(tagMap, key) + } + + // Convert back to slice sorted by key for stable output + tags := make([]serving.EndpointTag, 0, len(tagMap)) + keys := make([]string, 0, len(tagMap)) + for key := range tagMap { + keys = append(keys, key) + } + sort.Strings(keys) + for _, key := range keys { + tags = append(tags, serving.EndpointTag{Key: key, Value: tagMap[key]}) + } + + endpoint.Tags = tags + s.ServingEndpoints[name] = endpoint + + // Return the tags as EndpointTags struct, not as array + return Response{ + Body: serving.EndpointTags{ + Tags: tags, + }, + } +} From ea6dbec837f3581fe6df661d32ced7d61314fada Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 13:38:56 +0100 Subject: [PATCH 14/26] remove update testS --- .../update/description/databricks.yml.tmpl | 20 --- .../description/out.first-plan.direct.json | 28 ----- .../description/out.first-plan.terraform.json | 7 -- .../description/out.second-plan.direct.json | 35 ------ .../out.second-plan.terraform.json | 7 -- .../update/description/out.test.toml | 5 - .../update/description/output.txt | 85 ------------- .../update/description/script | 23 ---- .../served-entities/databricks.yml.tmpl | 19 --- .../out.first-plan.direct.json | 27 ---- .../out.first-plan.terraform.json | 7 -- .../out.second-plan.direct.json | 60 --------- .../out.second-plan.terraform.json | 7 -- .../update/served-entities/out.test.toml | 5 - .../update/served-entities/output.txt | 76 ------------ .../update/served-entities/script | 23 ---- .../update/tags/databricks.yml.tmpl | 24 ---- .../update/tags/out.first-plan.direct.json | 37 ------ .../update/tags/out.first-plan.terraform.json | 7 -- .../update/tags/out.second-plan.direct.json | 80 ------------ .../tags/out.second-plan.terraform.json | 7 -- .../update/tags/out.test.toml | 5 - .../update/tags/output.txt | 116 ------------------ .../update/tags/script | 23 ---- .../model_serving_endpoints/update/test.toml | 7 -- 25 files changed, 740 deletions(-) delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt delete mode 100755 acceptance/bundle/resources/model_serving_endpoints/update/description/script delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt delete mode 100755 acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt delete mode 100755 acceptance/bundle/resources/model_serving_endpoints/update/tags/script delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/update/test.toml diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl deleted file mode 100644 index 3ddd574a40..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/databricks.yml.tmpl +++ /dev/null @@ -1,20 +0,0 @@ -bundle: - name: test-mse-desc-$UNIQUE_NAME - -workspace: - root_path: ~/.bundle/$UNIQUE_NAME - -resources: - model_serving_endpoints: - test_endpoint: - name: test-endpoint-$UNIQUE_NAME - description: "Initial description" - config: - served_entities: - - name: prod - external_model: - name: gpt-4o-mini - provider: openai - task: llm/v1/chat - openai_config: - openai_api_key: "{{secrets/test-scope/openai-key}}" diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct.json deleted file mode 100644 index 5982335082..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.direct.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "create", - "new_state": { - "config": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "description": "Initial description", - "name": "[ENDPOINT_ID]" - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json deleted file mode 100644 index 82ea9497c9..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.first-plan.terraform.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "create" - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json deleted file mode 100644 index 3694e643f1..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.direct.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "recreate", - "new_state": { - "config": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "description": "Updated description", - "name": "[ENDPOINT_ID]" - } - }, - "changes": { - "local": { - "description": { - "action": "recreate" - } - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json deleted file mode 100644 index bc939a91b2..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.second-plan.terraform.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "update" - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml deleted file mode 100644 index d560f1de04..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/out.test.toml +++ /dev/null @@ -1,5 +0,0 @@ -Local = true -Cloud = false - -[EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt deleted file mode 100644 index a2ba67bafe..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/output.txt +++ /dev/null @@ -1,85 +0,0 @@ - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> [CLI] serving-endpoints get [ENDPOINT_ID] -"Initial description" - ->>> update_file.py databricks.yml description: "Initial description" description: "Updated description" - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> print_requests.py //serving-endpoints -{ - "method": "POST", - "path": "/api/2.0/serving-endpoints", - "body": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "description": "Initial description", - "name": "[ENDPOINT_ID]" - } -} -{ - "method": "DELETE", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]" -} -{ - "method": "POST", - "path": "/api/2.0/serving-endpoints", - "body": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "description": "Updated description", - "name": "[ENDPOINT_ID]" - } -} - ->>> [CLI] serving-endpoints get [ENDPOINT_ID] -"Updated description" - ->>> [CLI] bundle destroy --auto-approve -The following resources will be deleted: - delete model_serving_endpoint test_endpoint - -All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] - -Deleting files... -Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/description/script b/acceptance/bundle/resources/model_serving_endpoints/update/description/script deleted file mode 100755 index 7a60b33cb2..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/description/script +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -envsubst < databricks.yml.tmpl > databricks.yml - -cleanup() { - trace $CLI bundle destroy --auto-approve - rm -f out.requests.txt -} -trap cleanup EXIT - -trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json -trace $CLI bundle deploy - -ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') -echo "$ENDPOINT_ID:ENDPOINT_ID" >> ACC_REPLS -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.description' - -trace update_file.py databricks.yml 'description: "Initial description"' 'description: "Updated description"' -trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json -trace $CLI bundle deploy - -trace print_requests.py //serving-endpoints - -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.description' diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl deleted file mode 100644 index c1c7a26545..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/databricks.yml.tmpl +++ /dev/null @@ -1,19 +0,0 @@ -bundle: - name: test-mse-entities-$UNIQUE_NAME - -workspace: - root_path: ~/.bundle/$UNIQUE_NAME - -resources: - model_serving_endpoints: - test_endpoint: - name: test-endpoint-$UNIQUE_NAME - config: - served_entities: - - name: prod - external_model: - name: gpt-4o-mini - provider: openai - task: llm/v1/chat - openai_config: - openai_api_key: "{{secrets/test-scope/openai-key}}" diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct.json deleted file mode 100644 index 31f4ab3a45..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.direct.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "create", - "new_state": { - "config": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ENDPOINT_ID]" - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json deleted file mode 100644 index 82ea9497c9..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.first-plan.terraform.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "create" - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct.json deleted file mode 100644 index 25a39cf360..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.direct.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "update", - "new_state": { - "config": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ENDPOINT_ID]" - } - }, - "remote_state": { - "endpoint_details": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "creator": "[USERNAME]", - "id": "[UUID]", - "name": "[ENDPOINT_ID]", - "state": { - "config_update": "NOT_UPDATING" - } - }, - "endpoint_id": "[UUID]" - }, - "changes": { - "local": { - "config.served_entities[0].external_model.name": { - "action": "update" - } - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json deleted file mode 100644 index bc939a91b2..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.second-plan.terraform.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "update" - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml deleted file mode 100644 index d560f1de04..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/out.test.toml +++ /dev/null @@ -1,5 +0,0 @@ -Local = true -Cloud = false - -[EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt deleted file mode 100644 index 954dd0deb7..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/output.txt +++ /dev/null @@ -1,76 +0,0 @@ - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> [CLI] serving-endpoints get [ENDPOINT_ID] -"gpt-4o-mini" - ->>> update_file.py databricks.yml name: gpt-4o-mini name: gpt-4o - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> print_requests.py //serving-endpoints -{ - "method": "POST", - "path": "/api/2.0/serving-endpoints", - "body": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ENDPOINT_ID]" - } -} -{ - "method": "PUT", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config", - "body": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - } -} - ->>> [CLI] serving-endpoints get [ENDPOINT_ID] -"gpt-4o" - ->>> [CLI] bundle destroy --auto-approve -The following resources will be deleted: - delete model_serving_endpoint test_endpoint - -All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] - -Deleting files... -Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script b/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script deleted file mode 100755 index 0092bddace..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/served-entities/script +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -envsubst < databricks.yml.tmpl > databricks.yml - -cleanup() { - trace $CLI bundle destroy --auto-approve - rm -f out.requests.txt -} -trap cleanup EXIT - -trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json -trace $CLI bundle deploy - -ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') -echo "$ENDPOINT_ID:ENDPOINT_ID" >> ACC_REPLS -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.served_entities[0].external_model.name' - -trace update_file.py databricks.yml 'name: gpt-4o-mini' 'name: gpt-4o' -trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json -trace $CLI bundle deploy - -trace print_requests.py //serving-endpoints - -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.served_entities[0].external_model.name' diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl deleted file mode 100644 index a8c8597fc5..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/databricks.yml.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -bundle: - name: test-mse-tags-$UNIQUE_NAME - -workspace: - root_path: ~/.bundle/$UNIQUE_NAME - -resources: - model_serving_endpoints: - test_endpoint: - name: test-endpoint-$UNIQUE_NAME - config: - served_entities: - - name: prod - external_model: - name: gpt-4o-mini - provider: openai - task: llm/v1/chat - openai_config: - openai_api_key: "{{secrets/test-scope/openai-key}}" - tags: - - key: env - value: test - - key: team - value: ml diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct.json deleted file mode 100644 index 156d35a4b9..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.direct.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "create", - "new_state": { - "config": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ENDPOINT_ID]", - "tags": [ - { - "key": "env", - "value": "test" - }, - { - "key": "team", - "value": "ml" - } - ] - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json deleted file mode 100644 index 82ea9497c9..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.first-plan.terraform.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "create" - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct.json deleted file mode 100644 index adaf5bccb5..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.direct.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "update", - "new_state": { - "config": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ENDPOINT_ID]", - "tags": [ - { - "key": "env", - "value": "prod" - }, - { - "key": "team", - "value": "ml" - } - ] - } - }, - "remote_state": { - "endpoint_details": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "creator": "[USERNAME]", - "id": "[UUID]", - "name": "[ENDPOINT_ID]", - "state": { - "config_update": "NOT_UPDATING" - }, - "tags": [ - { - "key": "env", - "value": "test" - }, - { - "key": "team", - "value": "ml" - } - ] - }, - "endpoint_id": "[UUID]" - }, - "changes": { - "local": { - "tags[0].value": { - "action": "update" - } - } - } - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json deleted file mode 100644 index bc939a91b2..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.second-plan.terraform.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "plan": { - "resources.model_serving_endpoints.test_endpoint": { - "action": "update" - } - } -} diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml deleted file mode 100644 index d560f1de04..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.test.toml +++ /dev/null @@ -1,5 +0,0 @@ -Local = true -Cloud = false - -[EnvMatrix] - DATABRICKS_BUNDLE_ENGINE = ["terraform", "direct"] diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt b/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt deleted file mode 100644 index 92d89d40f0..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/output.txt +++ /dev/null @@ -1,116 +0,0 @@ - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> [CLI] serving-endpoints get [ENDPOINT_ID] -[ - { - "key": "env", - "value": "test" - }, - { - "key": "team", - "value": "ml" - } -] - ->>> update_file.py databricks.yml value: test value: prod - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - ->>> print_requests.py //serving-endpoints -{ - "method": "POST", - "path": "/api/2.0/serving-endpoints", - "body": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ENDPOINT_ID]", - "tags": [ - { - "key": "env", - "value": "test" - }, - { - "key": "team", - "value": "ml" - } - ] - } -} -{ - "method": "PUT", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/config", - "body": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - } -} -{ - "method": "PATCH", - "path": "/api/2.0/serving-endpoints/[ENDPOINT_ID]/tags", - "body": { - "add_tags": [ - { - "key": "env", - "value": "prod" - } - ] - } -} - ->>> [CLI] serving-endpoints get [ENDPOINT_ID] -[ - { - "key": "env", - "value": "prod" - }, - { - "key": "team", - "value": "ml" - } -] - ->>> [CLI] bundle destroy --auto-approve -The following resources will be deleted: - delete model_serving_endpoint test_endpoint - -All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME] - -Deleting files... -Destroy complete! diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/script b/acceptance/bundle/resources/model_serving_endpoints/update/tags/script deleted file mode 100755 index 42172e8af8..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/script +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -envsubst < databricks.yml.tmpl > databricks.yml - -cleanup() { - trace $CLI bundle destroy --auto-approve - rm -f out.requests.txt -} -trap cleanup EXIT - -trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json -trace $CLI bundle deploy - -ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') -echo "$ENDPOINT_ID:ENDPOINT_ID" >> ACC_REPLS -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.tags' - -trace update_file.py databricks.yml 'value: test' 'value: prod' -trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json -trace $CLI bundle deploy - -trace print_requests.py //serving-endpoints - -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.tags' diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/test.toml b/acceptance/bundle/resources/model_serving_endpoints/update/test.toml deleted file mode 100644 index 3aa4a19050..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/update/test.toml +++ /dev/null @@ -1,7 +0,0 @@ -Local = true -Cloud = false -RecordRequests = true - -Ignore = [ - "databricks.yml", -] From 9f6db59198b08a79fbca8117b88551087b2888cc Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 13:44:20 +0100 Subject: [PATCH 15/26] test server --- libs/testserver/serving_endpoints.go | 147 +++++++++++---------------- 1 file changed, 59 insertions(+), 88 deletions(-) diff --git a/libs/testserver/serving_endpoints.go b/libs/testserver/serving_endpoints.go index 6f2014ff50..d771a7e507 100644 --- a/libs/testserver/serving_endpoints.go +++ b/libs/testserver/serving_endpoints.go @@ -8,6 +8,59 @@ import ( "github.com/databricks/databricks-sdk-go/service/serving" ) +func servedEntitiesInputToOutput(input []serving.ServedEntityInput) []serving.ServedEntityOutput { + entities := make([]serving.ServedEntityOutput, len(input)) + for i, entity := range input { + entities[i] = serving.ServedEntityOutput{ + EntityName: entity.EntityName, + EntityVersion: entity.EntityVersion, + EnvironmentVars: entity.EnvironmentVars, + ExternalModel: entity.ExternalModel, + InstanceProfileArn: entity.InstanceProfileArn, + MaxProvisionedConcurrency: entity.MaxProvisionedConcurrency, + MaxProvisionedThroughput: entity.MaxProvisionedThroughput, + MinProvisionedConcurrency: entity.MinProvisionedConcurrency, + MinProvisionedThroughput: entity.MinProvisionedThroughput, + Name: entity.Name, + ProvisionedModelUnits: entity.ProvisionedModelUnits, + ScaleToZeroEnabled: entity.ScaleToZeroEnabled, + WorkloadSize: entity.WorkloadSize, + WorkloadType: entity.WorkloadType, + ForceSendFields: entity.ForceSendFields, + } + } + return entities +} + +func servedModelsInputToOutput(input []serving.ServedModelInput) []serving.ServedModelOutput { + models := make([]serving.ServedModelOutput, len(input)) + for i, model := range input { + models[i] = serving.ServedModelOutput{ + ModelName: model.ModelName, + ModelVersion: model.ModelVersion, + EnvironmentVars: model.EnvironmentVars, + InstanceProfileArn: model.InstanceProfileArn, + MaxProvisionedConcurrency: model.MaxProvisionedConcurrency, + MinProvisionedConcurrency: model.MinProvisionedConcurrency, + ProvisionedModelUnits: model.ProvisionedModelUnits, + ScaleToZeroEnabled: model.ScaleToZeroEnabled, + WorkloadSize: model.WorkloadSize, + WorkloadType: serving.ServingModelWorkloadType(model.WorkloadType), + ForceSendFields: model.ForceSendFields, + } + } + return models +} + +func autoCaptureConfigInputToOutput(input *serving.AutoCaptureConfigInput) *serving.AutoCaptureConfigOutput { + return &serving.AutoCaptureConfigOutput{ + CatalogName: input.CatalogName, + SchemaName: input.SchemaName, + TableNamePrefix: input.TableNamePrefix, + Enabled: input.Enabled, + ForceSendFields: input.ForceSendFields, + } + func (s *FakeWorkspace) ServingEndpointCreate(req Request) Response { defer s.LockUnlock()() @@ -37,58 +90,17 @@ func (s *FakeWorkspace) ServingEndpointCreate(req Request) Response { // Convert ServedEntityInput to ServedEntityOutput if len(createReq.Config.ServedEntities) > 0 { - config.ServedEntities = make([]serving.ServedEntityOutput, len(createReq.Config.ServedEntities)) - for i, entity := range createReq.Config.ServedEntities { - config.ServedEntities[i] = serving.ServedEntityOutput{ - EntityName: entity.EntityName, - EntityVersion: entity.EntityVersion, - EnvironmentVars: entity.EnvironmentVars, - ExternalModel: entity.ExternalModel, - InstanceProfileArn: entity.InstanceProfileArn, - MaxProvisionedConcurrency: entity.MaxProvisionedConcurrency, - MaxProvisionedThroughput: entity.MaxProvisionedThroughput, - MinProvisionedConcurrency: entity.MinProvisionedConcurrency, - MinProvisionedThroughput: entity.MinProvisionedThroughput, - Name: entity.Name, - ProvisionedModelUnits: entity.ProvisionedModelUnits, - ScaleToZeroEnabled: entity.ScaleToZeroEnabled, - WorkloadSize: entity.WorkloadSize, - WorkloadType: entity.WorkloadType, - ForceSendFields: entity.ForceSendFields, - } - } + config.ServedEntities = servedEntitiesInputToOutput(createReq.Config.ServedEntities) } // Convert ServedModelInput to ServedModelOutput if len(createReq.Config.ServedModels) > 0 { - config.ServedModels = make([]serving.ServedModelOutput, len(createReq.Config.ServedModels)) - for i, model := range createReq.Config.ServedModels { - config.ServedModels[i] = serving.ServedModelOutput{ - EnvironmentVars: model.EnvironmentVars, - InstanceProfileArn: model.InstanceProfileArn, - MaxProvisionedConcurrency: model.MaxProvisionedConcurrency, - MinProvisionedConcurrency: model.MinProvisionedConcurrency, - ModelName: model.ModelName, - ModelVersion: model.ModelVersion, - Name: model.Name, - ProvisionedModelUnits: model.ProvisionedModelUnits, - ScaleToZeroEnabled: model.ScaleToZeroEnabled, - WorkloadSize: model.WorkloadSize, - WorkloadType: serving.ServingModelWorkloadType(model.WorkloadType), - ForceSendFields: model.ForceSendFields, - } - } + config.ServedModels = servedModelsInputToOutput(createReq.Config.ServedModels) } // Convert AutoCaptureConfig if present if createReq.Config.AutoCaptureConfig != nil { - config.AutoCaptureConfig = &serving.AutoCaptureConfigOutput{ - CatalogName: createReq.Config.AutoCaptureConfig.CatalogName, - SchemaName: createReq.Config.AutoCaptureConfig.SchemaName, - TableNamePrefix: createReq.Config.AutoCaptureConfig.TableNamePrefix, - Enabled: createReq.Config.AutoCaptureConfig.Enabled, - ForceSendFields: createReq.Config.AutoCaptureConfig.ForceSendFields, - } + config.AutoCaptureConfig = autoCaptureConfigInputToOutput(createReq.Config.AutoCaptureConfig) } } @@ -145,58 +157,17 @@ func (s *FakeWorkspace) ServingEndpointUpdate(req Request, name string) Response // Convert ServedEntityInput to ServedEntityOutput if len(updateReq.ServedEntities) > 0 { - config.ServedEntities = make([]serving.ServedEntityOutput, len(updateReq.ServedEntities)) - for i, entity := range updateReq.ServedEntities { - config.ServedEntities[i] = serving.ServedEntityOutput{ - EntityName: entity.EntityName, - EntityVersion: entity.EntityVersion, - EnvironmentVars: entity.EnvironmentVars, - ExternalModel: entity.ExternalModel, - InstanceProfileArn: entity.InstanceProfileArn, - MaxProvisionedConcurrency: entity.MaxProvisionedConcurrency, - MaxProvisionedThroughput: entity.MaxProvisionedThroughput, - MinProvisionedConcurrency: entity.MinProvisionedConcurrency, - MinProvisionedThroughput: entity.MinProvisionedThroughput, - Name: entity.Name, - ProvisionedModelUnits: entity.ProvisionedModelUnits, - ScaleToZeroEnabled: entity.ScaleToZeroEnabled, - WorkloadSize: entity.WorkloadSize, - WorkloadType: entity.WorkloadType, - ForceSendFields: entity.ForceSendFields, - } - } + config.ServedEntities = servedEntitiesInputToOutput(updateReq.ServedEntities) } // Convert ServedModelInput to ServedModelOutput if len(updateReq.ServedModels) > 0 { - config.ServedModels = make([]serving.ServedModelOutput, len(updateReq.ServedModels)) - for i, model := range updateReq.ServedModels { - config.ServedModels[i] = serving.ServedModelOutput{ - EnvironmentVars: model.EnvironmentVars, - InstanceProfileArn: model.InstanceProfileArn, - MaxProvisionedConcurrency: model.MaxProvisionedConcurrency, - MinProvisionedConcurrency: model.MinProvisionedConcurrency, - ModelName: model.ModelName, - ModelVersion: model.ModelVersion, - Name: model.Name, - ProvisionedModelUnits: model.ProvisionedModelUnits, - ScaleToZeroEnabled: model.ScaleToZeroEnabled, - WorkloadSize: model.WorkloadSize, - WorkloadType: serving.ServingModelWorkloadType(model.WorkloadType), - ForceSendFields: model.ForceSendFields, - } - } + config.ServedModels = servedModelsInputToOutput(updateReq.ServedModels) } // Convert AutoCaptureConfig if present if updateReq.AutoCaptureConfig != nil { - config.AutoCaptureConfig = &serving.AutoCaptureConfigOutput{ - CatalogName: updateReq.AutoCaptureConfig.CatalogName, - SchemaName: updateReq.AutoCaptureConfig.SchemaName, - TableNamePrefix: updateReq.AutoCaptureConfig.TableNamePrefix, - Enabled: updateReq.AutoCaptureConfig.Enabled, - ForceSendFields: updateReq.AutoCaptureConfig.ForceSendFields, - } + config.AutoCaptureConfig = autoCaptureConfigInputToOutput(updateReq.AutoCaptureConfig) } } From 7ef43237b3ed891836c24f585b94fea05183f389 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 13:46:33 +0100 Subject: [PATCH 16/26] address comments --- libs/testserver/serving_endpoints.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libs/testserver/serving_endpoints.go b/libs/testserver/serving_endpoints.go index d771a7e507..86dfc4babe 100644 --- a/libs/testserver/serving_endpoints.go +++ b/libs/testserver/serving_endpoints.go @@ -54,12 +54,13 @@ func servedModelsInputToOutput(input []serving.ServedModelInput) []serving.Serve func autoCaptureConfigInputToOutput(input *serving.AutoCaptureConfigInput) *serving.AutoCaptureConfigOutput { return &serving.AutoCaptureConfigOutput{ - CatalogName: input.CatalogName, - SchemaName: input.SchemaName, + CatalogName: input.CatalogName, + SchemaName: input.SchemaName, TableNamePrefix: input.TableNamePrefix, - Enabled: input.Enabled, + Enabled: input.Enabled, ForceSendFields: input.ForceSendFields, } +} func (s *FakeWorkspace) ServingEndpointCreate(req Request) Response { defer s.LockUnlock()() From 08275783a00e5ba31fbad6ba06034f09aa8f7c0c Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 13:50:15 +0100 Subject: [PATCH 17/26] lint --- bundle/direct/dresources/model_serving_endpoint.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index b3e1f0b91d..7650c822f4 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -72,10 +72,12 @@ func configOutputToInput(output *serving.EndpointCoreConfigOutput) *serving.Endp return &serving.EndpointCoreConfigInput{ AutoCaptureConfig: autoCaptureConfigOutputToInput(output.AutoCaptureConfig), ServedEntities: servedEntitiesOutputToInput(output.ServedEntities), + ServedModels: nil, + TrafficConfig: output.TrafficConfig, + Name: "", } } -// TODO: Remap served_models to served_entities. func (*ResourceModelServingEndpoint) RemapState(state *RefreshOutput) *serving.CreateServingEndpoint { details := state.EndpointDetails // Map the remote state (ServingEndpointDetailed) to the local state (CreateServingEndpoint) From cf0aabbe90848e057693859cedf654fc193d6874 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 13:51:31 +0100 Subject: [PATCH 18/26] - --- bundle/direct/dresources/model_serving_endpoint.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index 7650c822f4..04468ee2ad 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -169,7 +169,7 @@ func (r *ResourceModelServingEndpoint) updateAiGateway(ctx context.Context, id s return nil } -// TODO: does unsetting config work properly? +// TODO(shreyas): Add acceptance tests for unsetting. Will be done once we add selective updates for these fields. func (r *ResourceModelServingEndpoint) updateConfig(ctx context.Context, id string, config *serving.EndpointCoreConfigInput) error { if config == nil { // Unset config in resource. From d48d41c4783dbf87bd42112104b74966e964adb7 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 15:58:38 +0100 Subject: [PATCH 19/26] lint --- .../dresources/model_serving_endpoint.go | 47 ++++++++++++------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index 04468ee2ad..a50c635dd2 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -92,6 +92,10 @@ func (*ResourceModelServingEndpoint) RemapState(state *RefreshOutput) *serving.C RouteOptimized: details.RouteOptimized, Tags: details.Tags, ForceSendFields: utils.FilterFields[serving.CreateServingEndpoint](details.ForceSendFields), + + // Rate limits are a deprecated field that are not returned by the API on GET calls. Thus we map them to nil. + // TODO(shreyas): Add a warning when users try setting top level rate limits. + RateLimits: nil, } } @@ -122,19 +126,11 @@ func (r *ResourceModelServingEndpoint) DoCreate(ctx context.Context, config *ser // waitForEndpointReady waits for the serving endpoint to be ready (not updating) func (r *ResourceModelServingEndpoint) waitForEndpointReady(ctx context.Context, name string) (*RefreshOutput, error) { - waiter := &serving.WaitGetServingEndpointNotUpdating[serving.ServingEndpointDetailed]{ - Response: &serving.ServingEndpointDetailed{Name: name}, - Name: name, - Poll: func(timeout time.Duration, callback func(*serving.ServingEndpointDetailed)) (*serving.ServingEndpointDetailed, error) { - return r.client.ServingEndpoints.WaitGetServingEndpointNotUpdating(ctx, name, timeout, callback) - }, - } - - // Model serving endpoints can take a long time to spin up. We match the timeout from TF here (35 minutes). - details, err := waiter.GetWithTimeout(35 * time.Minute) + details, err := r.client.ServingEndpoints.WaitGetServingEndpointNotUpdating(ctx, name, 35*time.Minute, nil) if err != nil { return nil, err } + return &RefreshOutput{ EndpointDetails: details, EndpointId: details.Id, @@ -148,7 +144,12 @@ func (r *ResourceModelServingEndpoint) WaitAfterCreate(ctx context.Context, conf func (r *ResourceModelServingEndpoint) updateAiGateway(ctx context.Context, id string, aiGateway *serving.AiGatewayConfig) error { if aiGateway == nil { req := serving.PutAiGatewayRequest{ - Name: id, + Name: id, + FallbackConfig: nil, + Guardrails: nil, + InferenceTableConfig: nil, + RateLimits: nil, + UsageTrackingConfig: nil, } _, err := r.client.ServingEndpoints.PutAiGateway(ctx, req) return err @@ -169,11 +170,17 @@ func (r *ResourceModelServingEndpoint) updateAiGateway(ctx context.Context, id s return nil } -// TODO(shreyas): Add acceptance tests for unsetting. Will be done once we add selective updates for these fields. +// TODO(shreyas): Add acceptance tests for update and for unsetting. Will be done once we add selective updates for these fields. func (r *ResourceModelServingEndpoint) updateConfig(ctx context.Context, id string, config *serving.EndpointCoreConfigInput) error { if config == nil { // Unset config in resource. - req := serving.EndpointCoreConfigInput{Name: id} + req := serving.EndpointCoreConfigInput{ + Name: id, + AutoCaptureConfig: nil, + ServedEntities: nil, + TrafficConfig: nil, + ServedModels: nil, + } _, err := r.client.ServingEndpoints.UpdateConfig(ctx, req) return err } @@ -203,7 +210,7 @@ func (r *ResourceModelServingEndpoint) updateNotifications(ctx context.Context, return nil } -func diffTags(currentTags []serving.EndpointTag, desiredTags []serving.EndpointTag) (addTags []serving.EndpointTag, deleteTags []string) { +func diffTags(currentTags, desiredTags []serving.EndpointTag) (addTags []serving.EndpointTag, deleteTags []string) { addTags = make([]serving.EndpointTag, 0) // build maps for easy lookup. @@ -220,11 +227,19 @@ func diffTags(currentTags []serving.EndpointTag, desiredTags []serving.EndpointT for key, desiredValue := range desiredTagsMap { v, ok := currentTagsMap[key] if !ok { - addTags = append(addTags, serving.EndpointTag{Key: key, Value: desiredValue}) + addTags = append(addTags, serving.EndpointTag{ + Key: key, + Value: desiredValue, + ForceSendFields: nil, + }) continue } if v != desiredValue { - addTags = append(addTags, serving.EndpointTag{Key: key, Value: desiredValue}) + addTags = append(addTags, serving.EndpointTag{ + Key: key, + Value: desiredValue, + ForceSendFields: nil, + }) } } From f977b724e76fcc18c178c2f15c89fdd1953d96ca Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 16:22:30 +0100 Subject: [PATCH 20/26] fix recreate route-optimized --- .../route-optimized/databricks.yml.tmpl | 12 ++-- .../route-optimized/out.first-get.direct.json | 1 + .../out.first-get.terraform.json | 1 + .../out.first-plan.direct.json | 14 ++--- .../out.first-requests.direct.json | 19 ++++++ .../out.first-requests.terraform.json | 18 ++++++ .../out.second-plan.direct.json | 14 ++--- .../recreate/route-optimized/output.txt | 58 ++++++++----------- .../recreate/route-optimized/script | 7 ++- .../recreate/route-optimized/test.toml | 3 + 10 files changed, 86 insertions(+), 61 deletions(-) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.direct.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.terraform.json create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl index 1b1c65025b..d20ffc62da 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/databricks.yml.tmpl @@ -10,11 +10,9 @@ resources: name: test-endpoint-$UNIQUE_NAME config: served_entities: - - name: prod - external_model: - name: gpt-4o-mini - provider: openai - task: llm/v1/chat - openai_config: - openai_api_key: "{{secrets/test-scope/openai-key}}" + - name: "llama" + entity_name: system.ai.llama_v3_2_1b_instruct + entity_version: 1 + workload_size: Small + scale_to_zero_enabled: true route_optimized: false diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json new file mode 100644 index 0000000000..c508d5366f --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json @@ -0,0 +1 @@ +false diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json new file mode 100644 index 0000000000..19765bd501 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json @@ -0,0 +1 @@ +null diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct.json index 5e8fd28ba0..c11b2e1980 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-plan.direct.json @@ -7,15 +7,11 @@ "config": { "served_entities": [ { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" + "entity_name": "system.ai.llama_v3_2_1b_instruct", + "entity_version": "1", + "name": "llama", + "scale_to_zero_enabled": true, + "workload_size": "Small" } ] }, diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.direct.json new file mode 100644 index 0000000000..a287e1d81e --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.direct.json @@ -0,0 +1,19 @@ +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "entity_name": "system.ai.llama_v3_2_1b_instruct", + "entity_version": "1", + "name": "llama", + "scale_to_zero_enabled": true, + "workload_size": "Small" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]", + "route_optimized": false + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.terraform.json new file mode 100644 index 0000000000..95e359ba4e --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-requests.terraform.json @@ -0,0 +1,18 @@ +{ + "method": "POST", + "path": "/api/2.0/serving-endpoints", + "body": { + "config": { + "served_entities": [ + { + "entity_name": "system.ai.llama_v3_2_1b_instruct", + "entity_version": "1", + "name": "llama", + "scale_to_zero_enabled": true, + "workload_size": "Small" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct.json index 8025fe7468..145e2879d7 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.second-plan.direct.json @@ -7,15 +7,11 @@ "config": { "served_entities": [ { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" + "entity_name": "system.ai.llama_v3_2_1b_instruct", + "entity_version": "1", + "name": "llama", + "scale_to_zero_enabled": true, + "workload_size": "Small" } ] }, diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt index cdda7bc15f..8c29071412 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/output.txt @@ -8,7 +8,6 @@ Updating deployment state... Deployment complete! >>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] -null >>> update_file.py databricks.yml route_optimized: false route_optimized: true @@ -21,28 +20,6 @@ Updating deployment state... Deployment complete! >>> print_requests.py //serving-endpoints -{ - "method": "POST", - "path": "/api/2.0/serving-endpoints", - "body": { - "config": { - "served_entities": [ - { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" - } - ] - }, - "name": "[ORIGINAL_ENDPOINT_ID]" - } -} { "method": "DELETE", "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" @@ -54,15 +31,11 @@ Deployment complete! "config": { "served_entities": [ { - "external_model": { - "name": "gpt-4o-mini", - "openai_config": { - "openai_api_key": "{{secrets/test-scope/openai-key}}" - }, - "provider": "openai", - "task": "llm/v1/chat" - }, - "name": "prod" + "entity_name": "system.ai.llama_v3_2_1b_instruct", + "entity_version": "1", + "name": "llama", + "scale_to_zero_enabled": true, + "workload_size": "Small" } ] }, @@ -72,7 +45,26 @@ Deployment complete! } >>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] -true +{ + "config": { + "served_entities": [ + { + "entity_name":"system.ai.llama_v3_2_1b_instruct", + "entity_version":"1", + "name":"llama", + "scale_to_zero_enabled":true, + "workload_size":"Small" + } + ] + }, + "creator":"[USERNAME]", + "id":"[UUID]", + "name":"[ORIGINAL_ENDPOINT_ID]", + "route_optimized":true, + "state": { + "config_update":"NOT_UPDATING" + } +} >>> [CLI] bundle destroy --auto-approve The following resources will be deleted: diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script index 7c3a61726a..584726a3a1 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script @@ -1,4 +1,3 @@ -#!/bin/bash envsubst < databricks.yml.tmpl > databricks.yml cleanup() { @@ -12,7 +11,9 @@ trace $CLI bundle deploy ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.route_optimized' +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.route_optimized' > out.first-get.$DATABRICKS_BUNDLE_ENGINE.json + +print_requests.py //serving-endpoints > out.first-requests.$DATABRICKS_BUNDLE_ENGINE.json trace update_file.py databricks.yml "route_optimized: false" "route_optimized: true" trace $CLI bundle debug plan > out.second-plan.$DATABRICKS_BUNDLE_ENGINE.json @@ -22,4 +23,4 @@ trace print_requests.py //serving-endpoints NEW_ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') echo "$NEW_ENDPOINT_ID:NEW_ENDPOINT_ID" >> ACC_REPLS -trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" | jq '.route_optimized' +trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml new file mode 100644 index 0000000000..8d88b07bdb --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml @@ -0,0 +1,3 @@ + +# [EnvMatrix] +# DATABRICKS_BUNDLE_ENGINE = ["direct"] From 4fb3923a4fcf1002b77c20d7ce10fb91abe6efb7 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 16:31:01 +0100 Subject: [PATCH 21/26] fix test --- .../catalog-name/out.first-plan.direct.json | 32 +++++++++++++++ .../catalog-name/out.second-plan.direct.json | 39 +++++++++++++++++++ .../recreate/catalog-name/output.txt | 2 +- .../recreate/catalog-name/script | 3 +- .../recreate/catalog-name/test.toml | 7 ---- 5 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct.json delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct.json index e69de29bb2..11afeef35c 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.first-plan.direct.json @@ -0,0 +1,32 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "create", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "main", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct.json new file mode 100644 index 0000000000..156261de42 --- /dev/null +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/out.second-plan.direct.json @@ -0,0 +1,39 @@ +{ + "plan": { + "resources.model_serving_endpoints.test_endpoint": { + "action": "recreate", + "new_state": { + "config": { + "config": { + "auto_capture_config": { + "catalog_name": "other_catalog", + "schema_name": "default", + "table_name_prefix": "my_table" + }, + "served_entities": [ + { + "external_model": { + "name": "gpt-4o-mini", + "openai_config": { + "openai_api_key": "{{secrets/test-scope/openai-key}}" + }, + "provider": "openai", + "task": "llm/v1/chat" + }, + "name": "prod" + } + ] + }, + "name": "[ORIGINAL_ENDPOINT_ID]" + } + }, + "changes": { + "local": { + "config.auto_capture_config.catalog_name": { + "action": "recreate" + } + } + } + } + } +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt index 311068305a..44a20e8641 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt @@ -1,5 +1,5 @@ ->>> CLI bundle debug plan +>>> [CLI] bundle debug plan >>> [CLI] bundle deploy Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script index 9221d02028..46c82cfa78 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script @@ -1,4 +1,3 @@ -#!/bin/bash envsubst < databricks.yml.tmpl > databricks.yml cleanup() { @@ -7,7 +6,7 @@ cleanup() { } trap cleanup EXIT -trace CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml deleted file mode 100644 index 3aa4a19050..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/test.toml +++ /dev/null @@ -1,7 +0,0 @@ -Local = true -Cloud = false -RecordRequests = true - -Ignore = [ - "databricks.yml", -] From 4a2bcfd7d9d313bca1e0185dc55d6dd301ebdc79 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 16:38:30 +0100 Subject: [PATCH 22/26] fix TestAll --- bundle/direct/dresources/all_test.go | 30 +++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/bundle/direct/dresources/all_test.go b/bundle/direct/dresources/all_test.go index 977c41bf9c..506be36c47 100644 --- a/bundle/direct/dresources/all_test.go +++ b/bundle/direct/dresources/all_test.go @@ -103,12 +103,32 @@ var testConfig map[string]any = map[string]any{ CreateServingEndpoint: serving.CreateServingEndpoint{ Name: "my-endpoint", Config: &serving.EndpointCoreConfigInput{ - ServedModels: []serving.ServedModelInput{ + Name: "my-endpoint", + AutoCaptureConfig: &serving.AutoCaptureConfigInput{ + CatalogName: "main", + SchemaName: "myschema", + TableNamePrefix: "my_table", + Enabled: true, + ForceSendFields: nil, + }, + ServedModels: nil, + ServedEntities: []serving.ServedEntityInput{ { - ModelName: "model-name", - ModelVersion: "1", - WorkloadSize: "Small", - ScaleToZeroEnabled: true, + EntityName: "entity-name", + EntityVersion: "1", + WorkloadSize: "Small", + ScaleToZeroEnabled: true, + WorkloadType: serving.ServingModelWorkloadTypeCpu, + EnvironmentVars: map[string]string{"key": "value"}, + InstanceProfileArn: "arn:aws:iam::123456789012:instance-profile/my-instance-profile", + MaxProvisionedConcurrency: 10, + MaxProvisionedThroughput: 100, + MinProvisionedConcurrency: 1, + MinProvisionedThroughput: 10, + Name: "entity-name", + ProvisionedModelUnits: 100, + ExternalModel: nil, + ForceSendFields: nil, }, }, TrafficConfig: &serving.TrafficConfig{ From 474e3b58428837ac3659c870190add6c49181ea3 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 16:42:55 +0100 Subject: [PATCH 23/26] - --- .../recreate/name-change/output.txt | 28 ++++++++++--------- .../recreate/name-change/script | 3 +- .../recreate/schema-name/output.txt | 28 ++++++++++--------- .../recreate/schema-name/script | 3 +- .../recreate/table-prefix/output.txt | 28 ++++++++++--------- .../recreate/table-prefix/script | 3 +- 6 files changed, 51 insertions(+), 42 deletions(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt index 662b3d488b..70320f337f 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/output.txt @@ -7,19 +7,6 @@ Deploying resources... Updating deployment state... Deployment complete! ->>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] -"[ORIGINAL_ENDPOINT_ID]" - ->>> update_file.py databricks.yml name: [ORIGINAL_ENDPOINT_ID] name: [NEW_ENDPOINT_ID] - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - >>> print_requests.py //serving-endpoints { "method": "POST", @@ -43,6 +30,21 @@ Deployment complete! "name": "[ORIGINAL_ENDPOINT_ID]" } } + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"[ORIGINAL_ENDPOINT_ID]" + +>>> update_file.py databricks.yml name: [ORIGINAL_ENDPOINT_ID] name: [NEW_ENDPOINT_ID] + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints { "method": "DELETE", "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script index e1fa06a187..fdbbeaa40d 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/script @@ -1,4 +1,3 @@ -#!/bin/bash envsubst < databricks.yml.tmpl > databricks.yml cleanup() { @@ -10,6 +9,8 @@ trap cleanup EXIT trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy +trace print_requests.py //serving-endpoints + ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.name' diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt index acb761dd69..edf577a5c4 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/output.txt @@ -7,19 +7,6 @@ Deploying resources... Updating deployment state... Deployment complete! ->>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] -"default" - ->>> update_file.py databricks.yml schema_name: default schema_name: other_schema - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - >>> print_requests.py //serving-endpoints { "method": "POST", @@ -48,6 +35,21 @@ Deployment complete! "name": "[ORIGINAL_ENDPOINT_ID]" } } + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"default" + +>>> update_file.py databricks.yml schema_name: default schema_name: other_schema + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints { "method": "DELETE", "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script index 07b52b2dd5..59c22a077a 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/script @@ -1,4 +1,3 @@ -#!/bin/bash envsubst < databricks.yml.tmpl > databricks.yml cleanup() { @@ -10,6 +9,8 @@ trap cleanup EXIT trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy +trace print_requests.py //serving-endpoints + ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.auto_capture_config.schema_name' diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt index dd102ba9fc..b178c70fb3 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/output.txt @@ -7,19 +7,6 @@ Deploying resources... Updating deployment state... Deployment complete! ->>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] -"my_table" - ->>> update_file.py databricks.yml table_name_prefix: my_table table_name_prefix: other_table - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - >>> print_requests.py //serving-endpoints { "method": "POST", @@ -48,6 +35,21 @@ Deployment complete! "name": "[ORIGINAL_ENDPOINT_ID]" } } + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"my_table" + +>>> update_file.py databricks.yml table_name_prefix: my_table table_name_prefix: other_table + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints { "method": "DELETE", "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script index d606520b3d..1d3f7b35a0 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/script @@ -1,4 +1,3 @@ -#!/bin/bash envsubst < databricks.yml.tmpl > databricks.yml cleanup() { @@ -10,6 +9,8 @@ trap cleanup EXIT trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy +trace print_requests.py //serving-endpoints + ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.auto_capture_config.table_name_prefix' From 167ccf430cb6fdd6d3b1ed63c99990f4e370bee2 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 16:44:39 +0100 Subject: [PATCH 24/26] cleanup --- .../model_serving_endpoints/recreate/route-optimized/test.toml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml deleted file mode 100644 index 8d88b07bdb..0000000000 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/test.toml +++ /dev/null @@ -1,3 +0,0 @@ - -# [EnvMatrix] -# DATABRICKS_BUNDLE_ENGINE = ["direct"] From 03228e7d519389344bb9890c72fddf442b672a57 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 16:57:18 +0100 Subject: [PATCH 25/26] - --- .../recreate/route-optimized/out.first-get.direct.json | 5 ++++- .../recreate/route-optimized/out.first-get.terraform.json | 5 ++++- .../model_serving_endpoints/recreate/route-optimized/script | 4 ++-- out.first-get..json | 0 4 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 out.first-get..json diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json index c508d5366f..f74ee87921 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.direct.json @@ -1 +1,4 @@ -false +{ + "route_optimized": false, + "id": "[UUID]" +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json index 19765bd501..f4d7f8991f 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/out.first-get.terraform.json @@ -1 +1,4 @@ -null +{ + "route_optimized": null, + "id": "[UUID]" +} diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script index 584726a3a1..fb93230a9e 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/route-optimized/script @@ -11,7 +11,7 @@ trace $CLI bundle deploy ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS -trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.route_optimized' > out.first-get.$DATABRICKS_BUNDLE_ENGINE.json +trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '{route_optimized, id}' > out.first-get.$DATABRICKS_BUNDLE_ENGINE.json print_requests.py //serving-endpoints > out.first-requests.$DATABRICKS_BUNDLE_ENGINE.json @@ -21,6 +21,6 @@ trace $CLI bundle deploy trace print_requests.py //serving-endpoints +# New and original endpoint ID should remain the same since they are both the name of the endpoint. NEW_ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') -echo "$NEW_ENDPOINT_ID:NEW_ENDPOINT_ID" >> ACC_REPLS trace $CLI serving-endpoints get "${NEW_ENDPOINT_ID}" diff --git a/out.first-get..json b/out.first-get..json new file mode 100644 index 0000000000..e69de29bb2 From 9b13fead536a4e26d002d93c2d3733e5e4e99f4c Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 10 Nov 2025 19:01:16 +0100 Subject: [PATCH 26/26] tests --- .../recreate/catalog-name/output.txt | 28 ++++++++++--------- .../recreate/catalog-name/script | 2 ++ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt index 44a20e8641..8688f1beee 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/output.txt @@ -7,19 +7,6 @@ Deploying resources... Updating deployment state... Deployment complete! ->>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] -"main" - ->>> update_file.py databricks.yml catalog_name: main catalog_name: other_catalog - ->>> [CLI] bundle debug plan - ->>> [CLI] bundle deploy -Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... -Deploying resources... -Updating deployment state... -Deployment complete! - >>> print_requests.py //serving-endpoints { "method": "POST", @@ -48,6 +35,21 @@ Deployment complete! "name": "[ORIGINAL_ENDPOINT_ID]" } } + +>>> [CLI] serving-endpoints get [ORIGINAL_ENDPOINT_ID] +"main" + +>>> update_file.py databricks.yml catalog_name: main catalog_name: other_catalog + +>>> [CLI] bundle debug plan + +>>> [CLI] bundle deploy +Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/[UNIQUE_NAME]/files... +Deploying resources... +Updating deployment state... +Deployment complete! + +>>> print_requests.py //serving-endpoints { "method": "DELETE", "path": "/api/2.0/serving-endpoints/[ORIGINAL_ENDPOINT_ID]" diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script index 46c82cfa78..e4a7b4276a 100755 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/catalog-name/script @@ -9,6 +9,8 @@ trap cleanup EXIT trace $CLI bundle debug plan > out.first-plan.$DATABRICKS_BUNDLE_ENGINE.json trace $CLI bundle deploy +trace print_requests.py //serving-endpoints + ENDPOINT_ID=$($CLI bundle summary -o json | jq -r '.resources.model_serving_endpoints.test_endpoint.id') echo "$ENDPOINT_ID:ORIGINAL_ENDPOINT_ID" >> ACC_REPLS trace $CLI serving-endpoints get "${ENDPOINT_ID}" | jq '.config.auto_capture_config.catalog_name'