diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index ea03b331d56..30031e4e8f7 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -14,6 +14,7 @@ * References to a registered model's `registered_model_id` now resolve under the direct engine, matching Terraform behavior ([#5621](https://github.com/databricks/cli/pull/5621)). * Fix missing field descriptions in the bundle JSON schema for fields whose upstream API docs arrived after the field was first annotated (e.g. `vector_search_endpoints.*.target_qps`); stale placeholder markers no longer hide them ([#5588](https://github.com/databricks/cli/pull/5588)). * Fix `bundle deploy --plan` dropping a `postgres_role`'s `role_id`, which caused the role to be recreated on the next deploy ([#5672](https://github.com/databricks/cli/pull/5672)). +* direct: Fix spurious cluster recreate when `apply_policy_default_values: true` is set ([#5693](https://github.com/databricks/cli/pull/5693)). ### Dependency updates * Bump `github.com/databricks/databricks-sdk-go` from v0.141.0 to v0.147.0 ([#5636](https://github.com/databricks/cli/pull/5636)). diff --git a/acceptance/bundle/invariant/configs/cluster_apply_policy_default_values.yml.tmpl b/acceptance/bundle/invariant/configs/cluster_apply_policy_default_values.yml.tmpl new file mode 100644 index 00000000000..57f8541f30f --- /dev/null +++ b/acceptance/bundle/invariant/configs/cluster_apply_policy_default_values.yml.tmpl @@ -0,0 +1,12 @@ +bundle: + name: test-bundle-$UNIQUE_NAME + +resources: + clusters: + foo: + cluster_name: test-cluster-$UNIQUE_NAME + spark_version: 13.3.x-scala2.12 + node_type_id: $NODE_TYPE_ID + instance_pool_id: $TEST_INSTANCE_POOL_ID + num_workers: 1 + apply_policy_default_values: true diff --git a/acceptance/bundle/invariant/continue_293/out.test.toml b/acceptance/bundle/invariant/continue_293/out.test.toml index e3c03283751..d9bdc5b830b 100644 --- a/acceptance/bundle/invariant/continue_293/out.test.toml +++ b/acceptance/bundle/invariant/continue_293/out.test.toml @@ -7,6 +7,7 @@ EnvMatrix.INPUT_CONFIG = [ "app.yml.tmpl", "catalog.yml.tmpl", "cluster.yml.tmpl", + "cluster_apply_policy_default_values.yml.tmpl", "dashboard.yml.tmpl", "database_catalog.yml.tmpl", "database_instance.yml.tmpl", diff --git a/acceptance/bundle/invariant/migrate/out.test.toml b/acceptance/bundle/invariant/migrate/out.test.toml index e3c03283751..d9bdc5b830b 100644 --- a/acceptance/bundle/invariant/migrate/out.test.toml +++ b/acceptance/bundle/invariant/migrate/out.test.toml @@ -7,6 +7,7 @@ EnvMatrix.INPUT_CONFIG = [ "app.yml.tmpl", "catalog.yml.tmpl", "cluster.yml.tmpl", + "cluster_apply_policy_default_values.yml.tmpl", "dashboard.yml.tmpl", "database_catalog.yml.tmpl", "database_instance.yml.tmpl", diff --git a/acceptance/bundle/invariant/no_drift/out.test.toml b/acceptance/bundle/invariant/no_drift/out.test.toml index f68ec6b3111..c4fb7e5bb95 100644 --- a/acceptance/bundle/invariant/no_drift/out.test.toml +++ b/acceptance/bundle/invariant/no_drift/out.test.toml @@ -7,6 +7,7 @@ EnvMatrix.INPUT_CONFIG = [ "app.yml.tmpl", "catalog.yml.tmpl", "cluster.yml.tmpl", + "cluster_apply_policy_default_values.yml.tmpl", "dashboard.yml.tmpl", "database_catalog.yml.tmpl", "database_instance.yml.tmpl", diff --git a/acceptance/bundle/invariant/test.toml b/acceptance/bundle/invariant/test.toml index e782f7ae269..f0971991b6a 100644 --- a/acceptance/bundle/invariant/test.toml +++ b/acceptance/bundle/invariant/test.toml @@ -25,6 +25,7 @@ EnvMatrix.INPUT_CONFIG = [ "app.yml.tmpl", "catalog.yml.tmpl", "cluster.yml.tmpl", + "cluster_apply_policy_default_values.yml.tmpl", "dashboard.yml.tmpl", "database_catalog.yml.tmpl", "database_instance.yml.tmpl", diff --git a/acceptance/bundle/refschema/out.fields.txt b/acceptance/bundle/refschema/out.fields.txt index a6776c50393..7d7b4913c9d 100644 --- a/acceptance/bundle/refschema/out.fields.txt +++ b/acceptance/bundle/refschema/out.fields.txt @@ -1731,148 +1731,148 @@ resources.jobs.*.permissions[*].group_name string ALL resources.jobs.*.permissions[*].level iam.PermissionLevel ALL resources.jobs.*.permissions[*].service_principal_name string ALL resources.jobs.*.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[*].burst_scaling_enabled bool 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[*].burst_scaling_enabled bool 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.*.ai_gateway *serving.AiGatewayConfig ALL +resources.model_serving_endpoints.*.ai_gateway.fallback_config *serving.FallbackConfig ALL +resources.model_serving_endpoints.*.ai_gateway.fallback_config.enabled bool ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails *serving.AiGatewayGuardrails ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input *serving.AiGatewayGuardrailParameters ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.invalid_keywords []string ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.invalid_keywords[*] string ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.pii *serving.AiGatewayGuardrailPiiBehavior ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.pii.behavior serving.AiGatewayGuardrailPiiBehaviorBehavior ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.safety bool ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.valid_topics []string ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.input.valid_topics[*] string ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output *serving.AiGatewayGuardrailParameters ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.invalid_keywords []string ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.invalid_keywords[*] string ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.pii *serving.AiGatewayGuardrailPiiBehavior ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.pii.behavior serving.AiGatewayGuardrailPiiBehaviorBehavior ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.safety bool ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.valid_topics []string ALL +resources.model_serving_endpoints.*.ai_gateway.guardrails.output.valid_topics[*] string ALL +resources.model_serving_endpoints.*.ai_gateway.inference_table_config *serving.AiGatewayInferenceTableConfig ALL +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.catalog_name string ALL +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.enabled bool ALL +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.schema_name string ALL +resources.model_serving_endpoints.*.ai_gateway.inference_table_config.table_name_prefix string ALL +resources.model_serving_endpoints.*.ai_gateway.rate_limits []serving.AiGatewayRateLimit ALL +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*] serving.AiGatewayRateLimit ALL +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].calls int64 ALL +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].key serving.AiGatewayRateLimitKey ALL +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].principal string ALL +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].renewal_period serving.AiGatewayRateLimitRenewalPeriod ALL +resources.model_serving_endpoints.*.ai_gateway.rate_limits[*].tokens int64 ALL +resources.model_serving_endpoints.*.ai_gateway.usage_tracking_config *serving.AiGatewayUsageTrackingConfig ALL +resources.model_serving_endpoints.*.ai_gateway.usage_tracking_config.enabled bool ALL +resources.model_serving_endpoints.*.budget_policy_id string ALL +resources.model_serving_endpoints.*.config *serving.EndpointCoreConfigInput ALL +resources.model_serving_endpoints.*.config.auto_capture_config *serving.AutoCaptureConfigInput ALL +resources.model_serving_endpoints.*.config.auto_capture_config.catalog_name string ALL +resources.model_serving_endpoints.*.config.auto_capture_config.enabled bool ALL +resources.model_serving_endpoints.*.config.auto_capture_config.schema_name string ALL +resources.model_serving_endpoints.*.config.auto_capture_config.table_name_prefix string ALL +resources.model_serving_endpoints.*.config.served_entities []serving.ServedEntityInput ALL +resources.model_serving_endpoints.*.config.served_entities[*] serving.ServedEntityInput ALL +resources.model_serving_endpoints.*.config.served_entities[*].burst_scaling_enabled bool ALL +resources.model_serving_endpoints.*.config.served_entities[*].entity_name string ALL +resources.model_serving_endpoints.*.config.served_entities[*].entity_version string ALL +resources.model_serving_endpoints.*.config.served_entities[*].environment_vars map[string]string ALL +resources.model_serving_endpoints.*.config.served_entities[*].environment_vars.* string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model *serving.ExternalModel ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.ai21labs_config *serving.Ai21LabsConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.ai21labs_config.ai21labs_api_key_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config *serving.AmazonBedrockConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_access_key_id_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_region string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.aws_secret_access_key_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.bedrock_provider serving.AmazonBedrockConfigBedrockProvider ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.amazon_bedrock_config.instance_profile_arn string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.anthropic_config *serving.AnthropicConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.anthropic_config.anthropic_api_key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.anthropic_config.anthropic_api_key_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config *serving.CohereConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config.cohere_api_base string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config.cohere_api_key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.cohere_config.cohere_api_key_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config *serving.CustomProviderConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth *serving.ApiKeyAuth ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth.key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth.value string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.api_key_auth.value_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth *serving.BearerTokenAuth ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.bearer_token_auth.token_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.custom_provider_config.custom_provider_url string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config *serving.DatabricksModelServingConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config.databricks_api_token_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.databricks_model_serving_config.databricks_workspace_url string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config *serving.GoogleCloudVertexAiConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.private_key_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.project_id string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.google_cloud_vertex_ai_config.region string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.name string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config *serving.OpenAiConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_client_id string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_client_secret_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.microsoft_entra_tenant_id string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_base string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_key_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_type string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_api_version string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_deployment_name string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.openai_config.openai_organization string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.palm_config *serving.PaLmConfig ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.palm_config.palm_api_key string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.palm_config.palm_api_key_plaintext string ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.provider serving.ExternalModelProvider ALL +resources.model_serving_endpoints.*.config.served_entities[*].external_model.task string ALL +resources.model_serving_endpoints.*.config.served_entities[*].instance_profile_arn string ALL +resources.model_serving_endpoints.*.config.served_entities[*].max_provisioned_concurrency int ALL +resources.model_serving_endpoints.*.config.served_entities[*].max_provisioned_throughput int ALL +resources.model_serving_endpoints.*.config.served_entities[*].min_provisioned_concurrency int ALL +resources.model_serving_endpoints.*.config.served_entities[*].min_provisioned_throughput int ALL +resources.model_serving_endpoints.*.config.served_entities[*].name string ALL +resources.model_serving_endpoints.*.config.served_entities[*].provisioned_model_units int64 ALL +resources.model_serving_endpoints.*.config.served_entities[*].scale_to_zero_enabled bool ALL +resources.model_serving_endpoints.*.config.served_entities[*].workload_size string ALL +resources.model_serving_endpoints.*.config.served_entities[*].workload_type serving.ServingModelWorkloadType ALL +resources.model_serving_endpoints.*.config.served_models []serving.ServedModelInput ALL +resources.model_serving_endpoints.*.config.served_models[*] serving.ServedModelInput ALL +resources.model_serving_endpoints.*.config.served_models[*].burst_scaling_enabled bool ALL +resources.model_serving_endpoints.*.config.served_models[*].environment_vars map[string]string ALL +resources.model_serving_endpoints.*.config.served_models[*].environment_vars.* string ALL +resources.model_serving_endpoints.*.config.served_models[*].instance_profile_arn string ALL +resources.model_serving_endpoints.*.config.served_models[*].max_provisioned_concurrency int ALL +resources.model_serving_endpoints.*.config.served_models[*].max_provisioned_throughput int ALL +resources.model_serving_endpoints.*.config.served_models[*].min_provisioned_concurrency int ALL +resources.model_serving_endpoints.*.config.served_models[*].min_provisioned_throughput int ALL +resources.model_serving_endpoints.*.config.served_models[*].model_name string ALL +resources.model_serving_endpoints.*.config.served_models[*].model_version string ALL +resources.model_serving_endpoints.*.config.served_models[*].name string ALL +resources.model_serving_endpoints.*.config.served_models[*].provisioned_model_units int64 ALL +resources.model_serving_endpoints.*.config.served_models[*].scale_to_zero_enabled bool ALL +resources.model_serving_endpoints.*.config.served_models[*].workload_size string ALL +resources.model_serving_endpoints.*.config.served_models[*].workload_type serving.ServedModelInputWorkloadType ALL +resources.model_serving_endpoints.*.config.traffic_config *serving.TrafficConfig ALL +resources.model_serving_endpoints.*.config.traffic_config.routes []serving.Route ALL +resources.model_serving_endpoints.*.config.traffic_config.routes[*] serving.Route ALL +resources.model_serving_endpoints.*.config.traffic_config.routes[*].served_entity_name string ALL +resources.model_serving_endpoints.*.config.traffic_config.routes[*].served_model_name string ALL +resources.model_serving_endpoints.*.config.traffic_config.routes[*].traffic_percentage int ALL +resources.model_serving_endpoints.*.description string ALL +resources.model_serving_endpoints.*.email_notifications *serving.EmailNotifications ALL +resources.model_serving_endpoints.*.email_notifications.on_update_failure []string ALL +resources.model_serving_endpoints.*.email_notifications.on_update_failure[*] string ALL +resources.model_serving_endpoints.*.email_notifications.on_update_success []string ALL +resources.model_serving_endpoints.*.email_notifications.on_update_success[*] string ALL 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 @@ -2181,17 +2181,17 @@ 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.*.name string ALL 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.*.route_optimized bool ALL +resources.model_serving_endpoints.*.tags []serving.EndpointTag ALL +resources.model_serving_endpoints.*.tags[*] serving.EndpointTag ALL +resources.model_serving_endpoints.*.tags[*].key string ALL +resources.model_serving_endpoints.*.tags[*].value string ALL resources.model_serving_endpoints.*.url string INPUT resources.model_serving_endpoints.*.permissions.object_id string ALL resources.model_serving_endpoints.*.permissions[*] dresources.StatePermission ALL @@ -3389,7 +3389,7 @@ resources.vector_search_endpoints.*.num_indexes int REMOTE resources.vector_search_endpoints.*.scaling_info *vectorsearch.EndpointScalingInfo REMOTE resources.vector_search_endpoints.*.scaling_info.requested_target_qps int64 REMOTE resources.vector_search_endpoints.*.scaling_info.state vectorsearch.ScalingChangeState REMOTE -resources.vector_search_endpoints.*.target_qps int64 INPUT STATE +resources.vector_search_endpoints.*.target_qps int64 ALL resources.vector_search_endpoints.*.url string INPUT resources.vector_search_endpoints.*.usage_policy_id string INPUT STATE resources.vector_search_endpoints.*.permissions.object_id string ALL 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 62806695822..1e06922d061 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 @@ -26,7 +26,8 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[ENDPOINT_ID_1]" + "endpoint_id": "[ENDPOINT_ID_1]", + "name": "[ENDPOINT_NAME_1]" }, "changes": { "description": { 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 index 2af8d4d7416..30f587571b4 100644 --- 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 @@ -66,7 +66,28 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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]" }, "changes": { "config.auto_capture_config.catalog_name": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct.json index 71fe34b4591..64624d0afc3 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/name-change/out.second-plan.direct.json @@ -56,7 +56,23 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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]" }, "changes": { "description": { 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 fdf9dc54d3b..d448e4bbaf7 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 @@ -49,7 +49,19 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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]" }, "changes": { "description": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct.json index 367d2028d50..e08b0601c0e 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/schema-name/out.second-plan.direct.json @@ -66,7 +66,28 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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]" }, "changes": { "config.auto_capture_config.schema_name": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct.json index eb0db4dd581..8c85604c02f 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/recreate/table-prefix/out.second-plan.direct.json @@ -66,7 +66,28 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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]" }, "changes": { "config.auto_capture_config.table_name_prefix": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.direct.json index a2aa883123f..f12f34c0fb5 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/ai-gateway/out.plan.direct.json @@ -66,7 +66,28 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "ai_gateway": { + "inference_table_config": { + "catalog_name": "first-inference-catalog" + } + }, + "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]" }, "changes": { "ai_gateway.inference_table_config.catalog_name": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.direct.json index 482f463aa99..243ff2f567f 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/both_gateway_and_tags/out.plan.direct.json @@ -78,7 +78,34 @@ } ] }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "ai_gateway": { + "inference_table_config": { + "catalog_name": "first-inference-catalog" + } + }, + "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": "team", + "value": "my-team-one" + } + ] }, "changes": { "ai_gateway.inference_table_config.catalog_name": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.direct.json index 4a34e191777..a6f72a06cd0 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/config/out.plan.direct.json @@ -56,7 +56,23 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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]" }, "changes": { "config.served_entities[0].external_model.name": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.direct.json index e43b533ebd9..e017672bbcf 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/email-notifications/out.plan.direct.json @@ -66,7 +66,28 @@ "ready": "NOT_READY" } }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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" + } + ] + }, + "email_notifications": { + "on_update_success": [ + "user1@example.com" + ] + }, + "name": "[ENDPOINT_ID]" }, "changes": { "description": { diff --git a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.direct.json b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.direct.json index f616085b3e4..ab92eedef09 100644 --- a/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.direct.json +++ b/acceptance/bundle/resources/model_serving_endpoints/update/tags/out.plan.direct.json @@ -68,7 +68,29 @@ } ] }, - "endpoint_id": "[UUID]" + "endpoint_id": "[UUID]", + "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": "team", + "value": "my-team-one" + } + ] }, "changes": { "description": { diff --git a/acceptance/bundle/resources/quality_monitors/change_assets_dir/out.plan.direct.json b/acceptance/bundle/resources/quality_monitors/change_assets_dir/out.plan.direct.json index 2f837c11f0d..af6b18f3a76 100644 --- a/acceptance/bundle/resources/quality_monitors/change_assets_dir/out.plan.direct.json +++ b/acceptance/bundle/resources/quality_monitors/change_assets_dir/out.plan.direct.json @@ -38,7 +38,7 @@ }, "warehouse_id": { "action": "skip", - "reason": "input_only", + "reason": "missing_in_remote", "old": "[TEST_DEFAULT_WAREHOUSE_ID]", "new": "[TEST_DEFAULT_WAREHOUSE_ID]" } diff --git a/acceptance/bundle/resources/quality_monitors/change_output_schema_name/out.plan.direct.json b/acceptance/bundle/resources/quality_monitors/change_output_schema_name/out.plan.direct.json index c4705197b10..4623690979d 100644 --- a/acceptance/bundle/resources/quality_monitors/change_output_schema_name/out.plan.direct.json +++ b/acceptance/bundle/resources/quality_monitors/change_output_schema_name/out.plan.direct.json @@ -35,7 +35,7 @@ }, "warehouse_id": { "action": "skip", - "reason": "input_only", + "reason": "missing_in_remote", "old": "[TEST_DEFAULT_WAREHOUSE_ID]", "new": "[TEST_DEFAULT_WAREHOUSE_ID]" } diff --git a/acceptance/bundle/resources/quality_monitors/change_table_name/out.plan.direct.json b/acceptance/bundle/resources/quality_monitors/change_table_name/out.plan.direct.json index be7a46eb1b3..ea8278b204a 100644 --- a/acceptance/bundle/resources/quality_monitors/change_table_name/out.plan.direct.json +++ b/acceptance/bundle/resources/quality_monitors/change_table_name/out.plan.direct.json @@ -38,7 +38,7 @@ }, "warehouse_id": { "action": "skip", - "reason": "input_only", + "reason": "missing_in_remote", "old": "[TEST_DEFAULT_WAREHOUSE_ID]", "new": "[TEST_DEFAULT_WAREHOUSE_ID]" } diff --git a/acceptance/bundle/resources/quality_monitors/create/out.plan_noop.direct.json b/acceptance/bundle/resources/quality_monitors/create/out.plan_noop.direct.json index 88053374383..e0d7c10dded 100644 --- a/acceptance/bundle/resources/quality_monitors/create/out.plan_noop.direct.json +++ b/acceptance/bundle/resources/quality_monitors/create/out.plan_noop.direct.json @@ -20,7 +20,7 @@ "changes": { "warehouse_id": { "action": "skip", - "reason": "input_only", + "reason": "missing_in_remote", "old": "[TEST_DEFAULT_WAREHOUSE_ID]", "new": "[TEST_DEFAULT_WAREHOUSE_ID]" } diff --git a/bundle/deployplan/plan.go b/bundle/deployplan/plan.go index 6254e92b802..c7fe8a3c989 100644 --- a/bundle/deployplan/plan.go +++ b/bundle/deployplan/plan.go @@ -103,6 +103,9 @@ const ( ReasonRemoteAlreadySet = "remote_already_set" ReasonEmpty = "empty" ReasonCustom = "custom" + // ReasonMissingInRemote: field is not present in RemoteType (write-only / input-only). + // Remote always appears nil, so treat the absence as a no-op when there is no local change. + ReasonMissingInRemote = "missing_in_remote" // Special reason that results in removing this change from the plan ReasonDrop = "!drop" diff --git a/bundle/direct/bundle_plan.go b/bundle/direct/bundle_plan.go index a9527ce56d5..d890b8d5d7b 100644 --- a/bundle/direct/bundle_plan.go +++ b/bundle/direct/bundle_plan.go @@ -387,6 +387,9 @@ func addPerFieldActions(ctx context.Context, adapter *dresources.Adapter, change } else if reason, ok := shouldSkipNormalized(generatedCfg, path, ch); ok { ch.Action = deployplan.Skip ch.Reason = reason + } else if isFieldMissingInRemote(adapter, path) && structdiff.IsEqual(ch.Old, ch.New) { + ch.Action = deployplan.Skip + ch.Reason = deployplan.ReasonMissingInRemote } else if reason, ok := findMatchingRule(path, cfg.RecreateOnChanges); ok { ch.Action = deployplan.Recreate ch.Reason = reason @@ -424,6 +427,15 @@ func addPerFieldActions(ctx context.Context, adapter *dresources.Adapter, change return nil } +// isFieldMissingInRemote reports whether path exists in StateType but is absent from RemoteType. +// Such fields are accepted by the API on write but not returned by GET. +func isFieldMissingInRemote(adapter *dresources.Adapter, path *structpath.PathNode) bool { + if structaccess.ValidatePath(adapter.StateType(), path) != nil { + return false + } + return structaccess.ValidatePath(adapter.RemoteType(), path) != nil +} + func findMatchingRule(path *structpath.PathNode, rules []dresources.FieldRule) (string, bool) { for _, r := range rules { if path.HasPatternPrefix(r.Field) { diff --git a/bundle/direct/dresources/README.md b/bundle/direct/dresources/README.md index 0425e52ec52..da9e9edab28 100644 --- a/bundle/direct/dresources/README.md +++ b/bundle/direct/dresources/README.md @@ -47,6 +47,18 @@ If the API may return a slice's elements in a different order between calls (e.g The state struct is serialized to JSON and persisted between deploys. Backward incompatible changes will result in a drift, which depending on field behaviour might result in recreate. See dstate/migrate.go on how to handle state migration. +## RemapState and missing remote fields + +Do not populate a field in `RemapState` by mapping it from a differently-named field in `RemoteType`. When a field is absent from `RemoteType` the engine automatically suppresses remote drift for it (reason: `missing_in_remote`), which means any value `RemapState` sets there is invisible to drift detection — real remote changes go undetected. + +The correct pattern is: + +1. Add the field to `RemoteType` (the struct returned by `DoRead`). +2. Populate it in `DoRead` by mapping from whatever the API returns under the other name. +3. Keep `RemapState` trivial (a direct struct copy or no-op). + +This makes the field present in `InputType`, `StateType`, and `RemoteType`, so it participates in normal drift detection and is no longer subject to the `missing_in_remote` suppression. + ## OverrideChangeDesc Use `OverrideChangeDesc` only as a last resort when `resources.yml` settings cannot express the needed logic. Skipping an action with `change.Action = deployplan.Skip` in `OverrideChangeDesc` creates a silent no-op: the plan shows no change even if the user's config differs from remote. Document the skip reason clearly in both the comment and `change.Reason`. diff --git a/bundle/direct/dresources/config_test.go b/bundle/direct/dresources/config_test.go index 5b063145e4c..706c100fde1 100644 --- a/bundle/direct/dresources/config_test.go +++ b/bundle/direct/dresources/config_test.go @@ -3,6 +3,7 @@ package dresources import ( "testing" + "github.com/databricks/cli/libs/structs/structaccess" "github.com/stretchr/testify/assert" ) @@ -74,6 +75,28 @@ func TestResourcesYMLNoRedundantRules(t *testing.T) { } } +// TestResourcesYMLNoRedundantMissingInRemote guards that ignore_remote_changes entries +// in resources.yml do not duplicate the automatic missing-in-remote suppression. A field +// absent from RemoteType is already skipped automatically (reason: missing_in_remote) when +// there is no local change, so a manual ignore_remote_changes entry for it is dead weight. +func TestResourcesYMLNoRedundantMissingInRemote(t *testing.T) { + cfg := MustLoadConfig() + for resourceType, rc := range cfg.Resources { + adapter, err := NewAdapter(SupportedResources[resourceType], resourceType, nil) + if err != nil { + t.Errorf("resources.yml: %s: failed to create adapter: %v", resourceType, err) + continue + } + for _, r := range rc.IgnoreRemoteChanges { + inState := structaccess.ValidatePattern(adapter.StateType(), r.Field) == nil + inRemote := structaccess.ValidatePattern(adapter.RemoteType(), r.Field) == nil + if inState && !inRemote { + t.Errorf("resources.yml: %s.ignore_remote_changes entry %q is automatically handled (field absent from RemoteType); remove it", resourceType, r.Field) + } + } + } +} + // TestResourcesYMLActionCategoriesExclusive guards that a field is in at most one // of the action categories that decide a change's action. They are not // independent: classifyIDField (provided_id_fields, updatable_id_fields) runs diff --git a/bundle/direct/dresources/model_serving_endpoint.go b/bundle/direct/dresources/model_serving_endpoint.go index ccab3a13dea..0a6c3513741 100644 --- a/bundle/direct/dresources/model_serving_endpoint.go +++ b/bundle/direct/dresources/model_serving_endpoint.go @@ -92,19 +92,16 @@ func configOutputToInput(output *serving.EndpointCoreConfigOutput) *serving.Endp } func (*ResourceModelServingEndpoint) RemapState(state *ModelServingEndpointRemote) *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: 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), + AiGateway: state.AiGateway, + BudgetPolicyId: state.BudgetPolicyId, + Config: state.Config, + Description: state.Description, + EmailNotifications: state.EmailNotifications, + Name: state.Name, + RouteOptimized: state.RouteOptimized, + Tags: state.Tags, + ForceSendFields: utils.FilterFields[serving.CreateServingEndpoint](state.EndpointDetails.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. @@ -115,6 +112,32 @@ func (*ResourceModelServingEndpoint) RemapState(state *ModelServingEndpointRemot type ModelServingEndpointRemote struct { EndpointDetails *serving.ServingEndpointDetailed `json:"endpoint_details"` EndpointId string `json:"endpoint_id"` + + // Fields mapped from EndpointDetails in DoRead so that RemapState is a direct copy + // and these fields participate in normal drift detection. + AiGateway *serving.AiGatewayConfig `json:"ai_gateway,omitempty"` + BudgetPolicyId string `json:"budget_policy_id,omitempty"` + Config *serving.EndpointCoreConfigInput `json:"config,omitempty"` + Description string `json:"description,omitempty"` + EmailNotifications *serving.EmailNotifications `json:"email_notifications,omitempty"` + Name string `json:"name,omitempty"` + RouteOptimized bool `json:"route_optimized,omitempty"` + Tags []serving.EndpointTag `json:"tags,omitempty"` +} + +func newModelServingEndpointRemote(details *serving.ServingEndpointDetailed) *ModelServingEndpointRemote { + return &ModelServingEndpointRemote{ + EndpointDetails: details, + EndpointId: details.Id, + 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, + } } func (r *ResourceModelServingEndpoint) DoRead(ctx context.Context, id string) (*ModelServingEndpointRemote, error) { @@ -122,10 +145,7 @@ func (r *ResourceModelServingEndpoint) DoRead(ctx context.Context, id string) (* if err != nil { return nil, err } - return &ModelServingEndpointRemote{ - EndpointDetails: endpoint, - EndpointId: endpoint.Id, - }, nil + return newModelServingEndpointRemote(endpoint), nil } func (r *ResourceModelServingEndpoint) DoCreate(ctx context.Context, config *serving.CreateServingEndpoint) (string, *ModelServingEndpointRemote, error) { @@ -143,11 +163,7 @@ func (r *ResourceModelServingEndpoint) waitForEndpointReady(ctx context.Context, if err != nil { return nil, err } - - return &ModelServingEndpointRemote{ - EndpointDetails: details, - EndpointId: details.Id, - }, nil + return newModelServingEndpointRemote(details), nil } func (r *ResourceModelServingEndpoint) WaitAfterCreate(ctx context.Context, id string, config *serving.CreateServingEndpoint) (*ModelServingEndpointRemote, error) { diff --git a/bundle/direct/dresources/resources.yml b/bundle/direct/dresources/resources.yml index 35aac1ebf5c..2001d47dc4f 100644 --- a/bundle/direct/dresources/resources.yml +++ b/bundle/direct/dresources/resources.yml @@ -235,17 +235,6 @@ resources: reason: immutable - field: route_optimized reason: immutable - ignore_remote_changes: - # https://github.com/databricks/terraform-provider-databricks/blob/4eba541abe1a9f50993ea7b9dd83874207e224a1/serving/resource_model_serving.go#L370 - # common.CustomizeSchemaPath(m, "config", "traffic_config").SetComputed() - # Routes have custom SuppressDiff (lines 387-388) - - field: config.traffic_config - reason: managed - - field: budget_policy_id - reason: no_update_api - # There is PublicPreview endpoints but we're not using it https://docs.databricks.com/api/workspace/servingendpoints/put - - field: rate_limits - reason: not_implemented ignore_local_changes: - field: budget_policy_id reason: no_update_api @@ -310,13 +299,6 @@ resources: recreate_on_changes: - field: assets_dir reason: immutable - ignore_remote_changes: - - field: warehouse_id - reason: input_only - # skip_builtin_dashboard is input-only, not returned by the Get API. - # TF preserves it from state; see resource_quality_monitor.go. - - field: skip_builtin_dashboard - reason: input_only catalogs: recreate_on_changes: @@ -363,10 +345,6 @@ resources: updatable_id_fields: - field: name reason: id_changes - ignore_remote_changes: - # skip_validation is input-only and not returned by the API - - field: skip_validation - reason: input_only volumes: provided_id_fields: @@ -608,12 +586,6 @@ resources: # project_id is immutable (part of hierarchical name, not in API spec) - field: project_id reason: id_field - ignore_remote_changes: - # purge_on_delete is a delete-time query parameter; not returned by GET. - # When the user changes it locally we still want the new value to land - # in state so the next destroy honors it. - - field: purge_on_delete - reason: input_only postgres_branches: provided_id_fields: @@ -622,10 +594,6 @@ resources: reason: id_field - field: branch_id reason: id_field - ignore_remote_changes: - # replace_existing is a create-time query parameter; not returned by GET. - - field: replace_existing - reason: input_only ignore_local_changes: # replace_existing only takes effect on create; toggling it later is a no-op. - field: replace_existing @@ -646,10 +614,6 @@ resources: reason: id_field - field: endpoint_id reason: id_field - ignore_remote_changes: - # replace_existing is a create-time query parameter; not returned by GET. - - field: replace_existing - reason: input_only ignore_local_changes: # replace_existing only takes effect on create; toggling it later is a no-op. - field: replace_existing diff --git a/bundle/direct/dresources/type_test.go b/bundle/direct/dresources/type_test.go index fee09ed119f..3474450ed3e 100644 --- a/bundle/direct/dresources/type_test.go +++ b/bundle/direct/dresources/type_test.go @@ -23,15 +23,7 @@ var knownMissingInRemoteType = map[string][]string{ "skip_validation", }, "model_serving_endpoints": { - "ai_gateway", - "budget_policy_id", - "config", - "description", - "email_notifications", - "name", "rate_limits", - "route_optimized", - "tags", }, "quality_monitors": { "skip_builtin_dashboard", @@ -53,7 +45,6 @@ var knownMissingInRemoteType = map[string][]string{ "purge_on_delete", }, "vector_search_endpoints": { - "target_qps", "usage_policy_id", }, } diff --git a/bundle/direct/dresources/vector_search_endpoint.go b/bundle/direct/dresources/vector_search_endpoint.go index b5a0ebb9e54..12470872b62 100644 --- a/bundle/direct/dresources/vector_search_endpoint.go +++ b/bundle/direct/dresources/vector_search_endpoint.go @@ -22,6 +22,9 @@ var ( type VectorSearchEndpointRemote struct { vectorsearch.EndpointInfo EndpointUuid string `json:"endpoint_uuid"` + // TargetQps is mapped from EndpointInfo.ScalingInfo.RequestedTargetQps in DoRead + // so that drift detection can compare it directly against the config field. + TargetQps int64 `json:"target_qps,omitempty"` } // Custom marshalers needed because embedded vectorsearch.EndpointInfo has its own @@ -35,9 +38,14 @@ func (s VectorSearchEndpointRemote) MarshalJSON() ([]byte, error) { } func newVectorSearchEndpointRemote(info *vectorsearch.EndpointInfo) *VectorSearchEndpointRemote { + var targetQps int64 + if info.ScalingInfo != nil { + targetQps = info.ScalingInfo.RequestedTargetQps + } return &VectorSearchEndpointRemote{ EndpointInfo: *info, EndpointUuid: info.Id, + TargetQps: targetQps, } } @@ -54,16 +62,12 @@ func (*ResourceVectorSearchEndpoint) PrepareState(input *resources.VectorSearchE } func (*ResourceVectorSearchEndpoint) RemapState(remote *VectorSearchEndpointRemote) *vectorsearch.CreateEndpoint { - var targetQps int64 - if remote.ScalingInfo != nil { - targetQps = remote.ScalingInfo.RequestedTargetQps - } return &vectorsearch.CreateEndpoint{ Name: remote.Name, EndpointType: remote.EndpointType, BudgetPolicyId: remote.BudgetPolicyId, UsagePolicyId: "", // Missing in remote - TargetQps: targetQps, + TargetQps: remote.TargetQps, ForceSendFields: utils.FilterFields[vectorsearch.CreateEndpoint](remote.ForceSendFields, "UsagePolicyId"), } }