Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEXT_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ To disable this, set the environment variable DATABRICKS_CACHE_ENABLED to false.
### CLI

### Bundles
* Add support for configuring app.yaml options for apps via bundle config ([#4271](https://github.com/databricks/cli/pull/4271))
* Enable caching user identity by default ([#4202](https://github.com/databricks/cli/pull/4202))
* Fix false positive folder permission warnings and make them more actionable ([#4216](https://github.com/databricks/cli/pull/4216))
* Pass additional Azure DevOps system variables ([#4236](https://github.com/databricks/cli/pull/4236))
Expand Down
7 changes: 7 additions & 0 deletions acceptance/bundle/refschema/out.fields.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ resources.apps.*.compute_size apps.ComputeSize ALL
resources.apps.*.compute_status *apps.ComputeStatus ALL
resources.apps.*.compute_status.message string ALL
resources.apps.*.compute_status.state apps.ComputeState ALL
resources.apps.*.config *resources.AppConfig INPUT
resources.apps.*.config.command []string INPUT
resources.apps.*.config.command[*] string INPUT
resources.apps.*.config.env []resources.AppEnvVar INPUT
resources.apps.*.config.env[*] resources.AppEnvVar INPUT
resources.apps.*.config.env[*].name string INPUT
resources.apps.*.config.env[*].value string INPUT
resources.apps.*.create_time string ALL
resources.apps.*.creator string ALL
resources.apps.*.default_source_code_path string ALL
Expand Down
10 changes: 10 additions & 0 deletions acceptance/bundle/resources/apps/inline_config/app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import os

from flask import Flask

app = Flask(__name__)


@app.route("/")
def home():
return "Hello from inline config app!"
7 changes: 7 additions & 0 deletions acceptance/bundle/resources/apps/inline_config/app/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
command:
- python
- app.py

env:
- name: MY_ENV_VAR
value: test_value_old
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
bundle:
name: app-inline-config-$UNIQUE_NAME

resources:
apps:
myapp:
name: $UNIQUE_NAME
description: my_app_description
source_code_path: ./app
config:
command: ["flask", "--app", "app", "run"]
env:
- name: MY_ENV_VAR
value: test_value
- name: ANOTHER_VAR
value: another_value

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions acceptance/bundle/resources/apps/inline_config/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@

>>> [CLI] bundle deploy
Uploading bundle files to /Workspace/Users/[USERNAME]/.bundle/app-inline-config-[UNIQUE_NAME]/default/files...
Deploying resources...
Updating deployment state...
Deployment complete!

>>> [CLI] apps get-deployment [UNIQUE_NAME] [DEPLOYMENT_ID]
{
"command": [
"flask",
"--app",
"app",
"run"
],
"env_vars": [
{
"name": "MY_ENV_VAR"
},
{
"name": "ANOTHER_VAR"
}
]
}

>>> [CLI] bundle destroy --auto-approve
The following resources will be deleted:
delete resources.apps.myapp

All files and directories at the following location will be deleted: /Workspace/Users/[USERNAME]/.bundle/app-inline-config-[UNIQUE_NAME]/default

Deleting files...
Destroy complete!
17 changes: 17 additions & 0 deletions acceptance/bundle/resources/apps/inline_config/script
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
envsubst < databricks.yml.tmpl > databricks.yml

cleanup() {
trace $CLI bundle destroy --auto-approve
rm out.requests.txt
}
trap cleanup EXIT

trace $CLI bundle deploy

APP_NAME=$($CLI bundle summary -o json | jq -r '.resources.apps.myapp.name')
$CLI bundle run myapp > /dev/null 2>&1

DEPLOYMENT_ID=$($CLI apps get "$APP_NAME" | jq -r '.active_deployment.deployment_id')
echo $DEPLOYMENT_ID:DEPLOYMENT_ID >> ACC_REPLS

trace $CLI apps get-deployment "$APP_NAME" "$DEPLOYMENT_ID" | jq '{command, env_vars}'
2 changes: 2 additions & 0 deletions acceptance/bundle/resources/apps/inline_config/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Local = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have it on testserver as well? Cloud-only tests are painful to update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a relatively useless test, as here we are confirming backend behaviour that it overwrites values in app.yaml with whatever is passed to the Deploy call

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. However, even if test itself is not useful, having local run means we can regenerate the output quickly with 'make test-update'.

The longer running 'make test-update-aws' skips tests that have Local=true.

Ideally, we are able to update all test output with local runs.

Cloud = true
24 changes: 24 additions & 0 deletions bundle/config/resources/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,25 @@ import (
"github.com/databricks/databricks-sdk-go/service/apps"
)

// AppConfig represents the inline app.yaml configuration structure.
// This matches the structure of an app.yaml file that can be used to configure how the app runs.
type AppConfig struct {
// Command specifies the command to run the app (e.g., ["streamlit", "run", "app.py"])
Command []string `json:"command,omitempty" yaml:"command,omitempty"`

// Env contains environment variables to set for the app
Env []AppEnvVar `json:"env,omitempty" yaml:"env,omitempty"`
}

// AppEnvVar represents an environment variable configuration for an app
type AppEnvVar struct {
// Name is the environment variable name
Name string `json:"name" yaml:"name"`

// Value is the environment variable value
Value string `json:"value" yaml:"value"`
}

type App struct {
BaseResource
apps.App // nolint App struct also defines Id and URL field with the same json tag "id" and "url"
Expand All @@ -18,6 +37,11 @@ type App struct {
// on local disk and to the corresponding workspace path during app deployment.
SourceCodePath string `json:"source_code_path"`

// Config represents inline app.yaml configuration for the app.
// When specified, this configuration is written to an app.yaml file in the source code path during deployment.
// This allows users to define app configuration directly in the bundle YAML instead of maintaining a separate app.yaml file.
Config *AppConfig `json:"config,omitempty"`

Permissions []AppPermission `json:"permissions,omitempty"`
}

Expand Down
14 changes: 14 additions & 0 deletions bundle/internal/schema/annotations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,20 @@ github.com/databricks/cli/bundle/config/resources.AlertPermission:
"user_name":
"description": |-
PLACEHOLDER
github.com/databricks/cli/bundle/config/resources.AppConfig:
"command":
"description": |-
PLACEHOLDER
"env":
"description": |-
PLACEHOLDER
github.com/databricks/cli/bundle/config/resources.AppEnvVar:
"name":
"description": |-
PLACEHOLDER
"value":
"description": |-
PLACEHOLDER
github.com/databricks/cli/bundle/config/resources.AppPermission:
"group_name":
"description": |-
Expand Down
1 change: 1 addition & 0 deletions bundle/internal/validation/generated/required_fields.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 31 additions & 11 deletions bundle/run/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,41 @@ func waitForDeploymentToComplete(ctx context.Context, w *databricks.WorkspaceCli
return nil
}

// buildAppDeployment creates an AppDeployment struct with inline config if provided
func (a *appRunner) buildAppDeployment() apps.AppDeployment {
deployment := apps.AppDeployment{
Mode: apps.AppDeploymentModeSnapshot,
SourceCodePath: a.app.SourceCodePath,
}

// Add inline config if provided
if a.app.Config != nil {
if len(a.app.Config.Command) > 0 {
deployment.Command = a.app.Config.Command
}

if len(a.app.Config.Env) > 0 {
deployment.EnvVars = make([]apps.EnvVar, len(a.app.Config.Env))
for i, env := range a.app.Config.Env {
deployment.EnvVars[i] = apps.EnvVar{
Name: env.Name,
Value: env.Value,
}
}
}
}

return deployment
}

func (a *appRunner) deploy(ctx context.Context) error {
app := a.app
b := a.bundle
w := b.WorkspaceClient()

sourceCodePath := app.SourceCodePath
wait, err := w.Apps.Deploy(ctx, apps.CreateAppDeploymentRequest{
AppName: app.Name,
AppDeployment: apps.AppDeployment{
Mode: apps.AppDeploymentModeSnapshot,
SourceCodePath: sourceCodePath,
},
AppName: app.Name,
AppDeployment: a.buildAppDeployment(),
})
// If deploy returns an error, then there's an active deployment in progress, wait for it to complete.
// For this we first need to get an app and its acrive and pending deployments and then wait for them.
Expand All @@ -187,11 +210,8 @@ func (a *appRunner) deploy(ctx context.Context) error {

// Now we can try to deploy the app again
wait, err = w.Apps.Deploy(ctx, apps.CreateAppDeploymentRequest{
AppName: app.Name,
AppDeployment: apps.AppDeployment{
Mode: apps.AppDeploymentModeSnapshot,
SourceCodePath: sourceCodePath,
},
AppName: app.Name,
AppDeployment: a.buildAppDeployment(),
})
if err != nil {
return err
Expand Down
61 changes: 61 additions & 0 deletions bundle/schema/jsonschema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.