mirror of https://github.com/databricks/cli.git
Add a textual bundle summary command
This commit is contained in:
parent
84b47745e4
commit
869637dc54
|
@ -0,0 +1,62 @@
|
||||||
|
package mutator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/libs/diag"
|
||||||
|
)
|
||||||
|
|
||||||
|
type initializeUrls struct {
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeURLs makes sure the URL field of each resource is configured.
|
||||||
|
// NOTE: since this depends on an extra API call, this mutator adds some extra
|
||||||
|
// latency. As such, it should only be used when needed.
|
||||||
|
// This URL field is used for the output of the 'bundle summary' CLI command.
|
||||||
|
func InitializeURLs() bundle.Mutator {
|
||||||
|
return &initializeUrls{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *initializeUrls) Name() string {
|
||||||
|
return fmt.Sprintf("ConfigureURLs(%s)", m.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *initializeUrls) Apply(ctx context.Context, b *bundle.Bundle) diag.Diagnostics {
|
||||||
|
workspaceId, err := b.WorkspaceClient().CurrentWorkspaceID(ctx)
|
||||||
|
orgId := strconv.FormatInt(workspaceId, 10)
|
||||||
|
if err != nil {
|
||||||
|
return diag.FromErr(err)
|
||||||
|
}
|
||||||
|
configureForOrgId(b, orgId)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configureForOrgId(b *bundle.Bundle, orgId string) {
|
||||||
|
urlPrefix := b.Config.Workspace.Host
|
||||||
|
if !strings.HasSuffix(urlPrefix, "/") {
|
||||||
|
urlPrefix += "/"
|
||||||
|
}
|
||||||
|
urlSuffix := ""
|
||||||
|
|
||||||
|
// Add ?o=<workspace id> only if <workspace id> wasn't in the subdomain already.
|
||||||
|
// The ?o= is needed when vanity URLs / legacy workspace URLs are used.
|
||||||
|
// If it's not needed we prefer to leave it out since these URLs are rather
|
||||||
|
// long for most terminals.
|
||||||
|
//
|
||||||
|
// See https://docs.databricks.com/en/workspace/workspace-details.html for
|
||||||
|
// further reading about the '?o=' suffix.
|
||||||
|
if !strings.Contains(urlPrefix, orgId) {
|
||||||
|
urlSuffix = "?o=" + orgId
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, rs := range b.Config.Resources.AllResources() {
|
||||||
|
for _, r := range rs {
|
||||||
|
r.InitializeURL(urlPrefix, urlSuffix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
package mutator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/bundle/config"
|
||||||
|
"github.com/databricks/cli/bundle/config/resources"
|
||||||
|
"github.com/databricks/databricks-sdk-go/service/catalog"
|
||||||
|
"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/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIntitializeURLs(t *testing.T) {
|
||||||
|
b := &bundle.Bundle{
|
||||||
|
Config: config.Root{
|
||||||
|
Workspace: config.Workspace{
|
||||||
|
Host: "https://mycompany.databricks.com/",
|
||||||
|
},
|
||||||
|
Resources: config.Resources{
|
||||||
|
Jobs: map[string]*resources.Job{
|
||||||
|
"job1": {
|
||||||
|
ID: "1",
|
||||||
|
JobSettings: &jobs.JobSettings{Name: "job1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Pipelines: map[string]*resources.Pipeline{
|
||||||
|
"pipeline1": {
|
||||||
|
ID: "3",
|
||||||
|
PipelineSpec: &pipelines.PipelineSpec{Name: "pipeline1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Experiments: map[string]*resources.MlflowExperiment{
|
||||||
|
"experiment1": {
|
||||||
|
ID: "4",
|
||||||
|
Experiment: &ml.Experiment{Name: "experiment1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Models: map[string]*resources.MlflowModel{
|
||||||
|
"model1": {
|
||||||
|
ID: "6",
|
||||||
|
Model: &ml.Model{Name: "model1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ModelServingEndpoints: map[string]*resources.ModelServingEndpoint{
|
||||||
|
"servingendpoint1": {
|
||||||
|
ID: "7",
|
||||||
|
CreateServingEndpoint: &serving.CreateServingEndpoint{
|
||||||
|
Name: "my_serving_endpoint",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RegisteredModels: map[string]*resources.RegisteredModel{
|
||||||
|
"registeredmodel1": {
|
||||||
|
ID: "8",
|
||||||
|
CreateRegisteredModelRequest: &catalog.CreateRegisteredModelRequest{
|
||||||
|
Name: "my_registered_model",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
QualityMonitors: map[string]*resources.QualityMonitor{
|
||||||
|
"qualityMonitor1": {
|
||||||
|
CreateMonitor: &catalog.CreateMonitor{
|
||||||
|
TableName: "catalog.schema.qualityMonitor1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Schemas: map[string]*resources.Schema{
|
||||||
|
"schema1": {
|
||||||
|
ID: "catalog.schema",
|
||||||
|
CreateSchema: &catalog.CreateSchema{
|
||||||
|
Name: "schema",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedURLs := map[string]string{
|
||||||
|
"job1": "https://mycompany.databricks.com/jobs/1?o=123456",
|
||||||
|
"pipeline1": "https://mycompany.databricks.com/pipelines/3?o=123456",
|
||||||
|
"experiment1": "https://mycompany.databricks.com/ml/experiments/4?o=123456",
|
||||||
|
"model1": "https://mycompany.databricks.com/ml/models/model1?o=123456",
|
||||||
|
"servingendpoint1": "https://mycompany.databricks.com/ml/endpoints/my_serving_endpoint?o=123456",
|
||||||
|
"registeredmodel1": "https://mycompany.databricks.com/explore/data/models/8?o=123456",
|
||||||
|
"qualityMonitor1": "https://mycompany.databricks.com/explore/data/catalog/schema/qualityMonitor1?o=123456",
|
||||||
|
"schema1": "https://mycompany.databricks.com/explore/data/catalog/schema?o=123456",
|
||||||
|
}
|
||||||
|
|
||||||
|
configureForOrgId(b, "123456")
|
||||||
|
|
||||||
|
for _, rs := range b.Config.Resources.AllResources() {
|
||||||
|
for key, r := range rs {
|
||||||
|
require.Equal(t, expectedURLs[key], r.GetURL(), "Unexpected URL for "+key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntitializeURLs_NoOrgId(t *testing.T) {
|
||||||
|
b := &bundle.Bundle{
|
||||||
|
Config: config.Root{
|
||||||
|
Workspace: config.Workspace{
|
||||||
|
// Hostname with org id in URL and no trailing /
|
||||||
|
Host: "https://adb-123456.azuredatabricks.net",
|
||||||
|
},
|
||||||
|
Resources: config.Resources{
|
||||||
|
Jobs: map[string]*resources.Job{
|
||||||
|
"job1": {
|
||||||
|
ID: "1",
|
||||||
|
JobSettings: &jobs.JobSettings{Name: "job1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
configureForOrgId(b, "123456")
|
||||||
|
|
||||||
|
require.Equal(t, "https://adb-123456.azuredatabricks.net/jobs/1", b.Config.Resources.Jobs["job1"].URL)
|
||||||
|
}
|
|
@ -29,6 +29,69 @@ type ConfigResource interface {
|
||||||
// Terraform equivalent name of the resource. For example "databricks_job"
|
// Terraform equivalent name of the resource. For example "databricks_job"
|
||||||
// for jobs and "databricks_pipeline" for pipelines.
|
// for jobs and "databricks_pipeline" for pipelines.
|
||||||
TerraformResourceName() string
|
TerraformResourceName() string
|
||||||
|
|
||||||
|
// GetName returns the in-product name of the resource.
|
||||||
|
GetName() string
|
||||||
|
|
||||||
|
// GetURL returns the URL of the resource.
|
||||||
|
GetURL() string
|
||||||
|
|
||||||
|
// InitializeURL initializes the URL field of the resource.
|
||||||
|
InitializeURL(urlPrefix string, urlSuffix string)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Resources) AllResources() map[string]map[string]ConfigResource {
|
||||||
|
result := make(map[string]map[string]ConfigResource)
|
||||||
|
|
||||||
|
jobResources := make(map[string]ConfigResource)
|
||||||
|
for key, job := range r.Jobs {
|
||||||
|
jobResources[key] = job
|
||||||
|
}
|
||||||
|
result["jobs"] = jobResources
|
||||||
|
|
||||||
|
pipelineResources := make(map[string]ConfigResource)
|
||||||
|
for key, pipeline := range r.Pipelines {
|
||||||
|
pipelineResources[key] = pipeline
|
||||||
|
}
|
||||||
|
result["pipelines"] = pipelineResources
|
||||||
|
|
||||||
|
modelResources := make(map[string]ConfigResource)
|
||||||
|
for key, model := range r.Models {
|
||||||
|
modelResources[key] = model
|
||||||
|
}
|
||||||
|
result["models"] = modelResources
|
||||||
|
|
||||||
|
experimentResources := make(map[string]ConfigResource)
|
||||||
|
for key, experiment := range r.Experiments {
|
||||||
|
experimentResources[key] = experiment
|
||||||
|
}
|
||||||
|
result["experiments"] = experimentResources
|
||||||
|
|
||||||
|
modelServingEndpointResources := make(map[string]ConfigResource)
|
||||||
|
for key, endpoint := range r.ModelServingEndpoints {
|
||||||
|
modelServingEndpointResources[key] = endpoint
|
||||||
|
}
|
||||||
|
result["model_serving_endpoints"] = modelServingEndpointResources
|
||||||
|
|
||||||
|
registeredModelResources := make(map[string]ConfigResource)
|
||||||
|
for key, registeredModel := range r.RegisteredModels {
|
||||||
|
registeredModelResources[key] = registeredModel
|
||||||
|
}
|
||||||
|
result["registered_models"] = registeredModelResources
|
||||||
|
|
||||||
|
qualityMonitorResources := make(map[string]ConfigResource)
|
||||||
|
for key, qualityMonitor := range r.QualityMonitors {
|
||||||
|
qualityMonitorResources[key] = qualityMonitor
|
||||||
|
}
|
||||||
|
result["quality_monitors"] = qualityMonitorResources
|
||||||
|
|
||||||
|
schemaResources := make(map[string]ConfigResource)
|
||||||
|
for key, schema := range r.Schemas {
|
||||||
|
schemaResources[key] = schema
|
||||||
|
}
|
||||||
|
result["schemas"] = schemaResources
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resources) FindResourceByConfigKey(key string) (ConfigResource, error) {
|
func (r *Resources) FindResourceByConfigKey(key string) (ConfigResource, error) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ type Job struct {
|
||||||
ID string `json:"id,omitempty" bundle:"readonly"`
|
ID string `json:"id,omitempty" bundle:"readonly"`
|
||||||
Permissions []Permission `json:"permissions,omitempty"`
|
Permissions []Permission `json:"permissions,omitempty"`
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
|
|
||||||
*jobs.JobSettings
|
*jobs.JobSettings
|
||||||
}
|
}
|
||||||
|
@ -44,3 +45,18 @@ func (j *Job) Exists(ctx context.Context, w *databricks.WorkspaceClient, id stri
|
||||||
func (j *Job) TerraformResourceName() string {
|
func (j *Job) TerraformResourceName() string {
|
||||||
return "databricks_job"
|
return "databricks_job"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (j *Job) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if j.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
j.URL = urlPrefix + "jobs/" + j.ID + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Job) GetName() string {
|
||||||
|
return j.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (j *Job) GetURL() string {
|
||||||
|
return j.URL
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ type MlflowExperiment struct {
|
||||||
ID string `json:"id,omitempty" bundle:"readonly"`
|
ID string `json:"id,omitempty" bundle:"readonly"`
|
||||||
Permissions []Permission `json:"permissions,omitempty"`
|
Permissions []Permission `json:"permissions,omitempty"`
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
|
|
||||||
*ml.Experiment
|
*ml.Experiment
|
||||||
}
|
}
|
||||||
|
@ -39,3 +40,18 @@ func (s *MlflowExperiment) Exists(ctx context.Context, w *databricks.WorkspaceCl
|
||||||
func (s *MlflowExperiment) TerraformResourceName() string {
|
func (s *MlflowExperiment) TerraformResourceName() string {
|
||||||
return "databricks_mlflow_experiment"
|
return "databricks_mlflow_experiment"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MlflowExperiment) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if s.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.URL = urlPrefix + "ml/experiments/" + s.ID + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MlflowExperiment) GetName() string {
|
||||||
|
return s.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MlflowExperiment) GetURL() string {
|
||||||
|
return s.URL
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ type MlflowModel struct {
|
||||||
ID string `json:"id,omitempty" bundle:"readonly"`
|
ID string `json:"id,omitempty" bundle:"readonly"`
|
||||||
Permissions []Permission `json:"permissions,omitempty"`
|
Permissions []Permission `json:"permissions,omitempty"`
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
|
|
||||||
*ml.Model
|
*ml.Model
|
||||||
}
|
}
|
||||||
|
@ -39,3 +40,18 @@ func (s *MlflowModel) Exists(ctx context.Context, w *databricks.WorkspaceClient,
|
||||||
func (s *MlflowModel) TerraformResourceName() string {
|
func (s *MlflowModel) TerraformResourceName() string {
|
||||||
return "databricks_mlflow_model"
|
return "databricks_mlflow_model"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MlflowModel) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if s.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.URL = urlPrefix + "ml/models/" + s.Name + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MlflowModel) GetName() string {
|
||||||
|
return s.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MlflowModel) GetURL() string {
|
||||||
|
return s.URL
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ type ModelServingEndpoint struct {
|
||||||
Permissions []Permission `json:"permissions,omitempty"`
|
Permissions []Permission `json:"permissions,omitempty"`
|
||||||
|
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ModelServingEndpoint) UnmarshalJSON(b []byte) error {
|
func (s *ModelServingEndpoint) UnmarshalJSON(b []byte) error {
|
||||||
|
@ -47,3 +48,18 @@ func (s *ModelServingEndpoint) Exists(ctx context.Context, w *databricks.Workspa
|
||||||
func (s *ModelServingEndpoint) TerraformResourceName() string {
|
func (s *ModelServingEndpoint) TerraformResourceName() string {
|
||||||
return "databricks_model_serving"
|
return "databricks_model_serving"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *ModelServingEndpoint) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if s.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.URL = urlPrefix + "ml/endpoints/" + s.Name + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ModelServingEndpoint) GetName() string {
|
||||||
|
return s.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *ModelServingEndpoint) GetURL() string {
|
||||||
|
return s.URL
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ type Pipeline struct {
|
||||||
ID string `json:"id,omitempty" bundle:"readonly"`
|
ID string `json:"id,omitempty" bundle:"readonly"`
|
||||||
Permissions []Permission `json:"permissions,omitempty"`
|
Permissions []Permission `json:"permissions,omitempty"`
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
|
|
||||||
*pipelines.PipelineSpec
|
*pipelines.PipelineSpec
|
||||||
}
|
}
|
||||||
|
@ -39,3 +40,18 @@ func (p *Pipeline) Exists(ctx context.Context, w *databricks.WorkspaceClient, id
|
||||||
func (p *Pipeline) TerraformResourceName() string {
|
func (p *Pipeline) TerraformResourceName() string {
|
||||||
return "databricks_pipeline"
|
return "databricks_pipeline"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Pipeline) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if p.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
p.URL = urlPrefix + "pipelines/" + p.ID + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pipeline) GetName() string {
|
||||||
|
return p.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Pipeline) GetURL() string {
|
||||||
|
return s.URL
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package resources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/databricks/cli/libs/log"
|
"github.com/databricks/cli/libs/log"
|
||||||
"github.com/databricks/databricks-sdk-go"
|
"github.com/databricks/databricks-sdk-go"
|
||||||
|
@ -20,6 +21,7 @@ type QualityMonitor struct {
|
||||||
ID string `json:"id,omitempty" bundle:"readonly"`
|
ID string `json:"id,omitempty" bundle:"readonly"`
|
||||||
|
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *QualityMonitor) UnmarshalJSON(b []byte) error {
|
func (s *QualityMonitor) UnmarshalJSON(b []byte) error {
|
||||||
|
@ -44,3 +46,18 @@ func (s *QualityMonitor) Exists(ctx context.Context, w *databricks.WorkspaceClie
|
||||||
func (s *QualityMonitor) TerraformResourceName() string {
|
func (s *QualityMonitor) TerraformResourceName() string {
|
||||||
return "databricks_quality_monitor"
|
return "databricks_quality_monitor"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *QualityMonitor) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if s.TableName == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.URL = urlPrefix + "explore/data/" + strings.ReplaceAll(s.TableName, ".", "/") + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QualityMonitor) GetName() string {
|
||||||
|
return s.TableName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *QualityMonitor) GetURL() string {
|
||||||
|
return s.URL
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package resources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/databricks/cli/libs/log"
|
"github.com/databricks/cli/libs/log"
|
||||||
"github.com/databricks/databricks-sdk-go"
|
"github.com/databricks/databricks-sdk-go"
|
||||||
|
@ -24,6 +25,7 @@ type RegisteredModel struct {
|
||||||
*catalog.CreateRegisteredModelRequest
|
*catalog.CreateRegisteredModelRequest
|
||||||
|
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *RegisteredModel) UnmarshalJSON(b []byte) error {
|
func (s *RegisteredModel) UnmarshalJSON(b []byte) error {
|
||||||
|
@ -48,3 +50,18 @@ func (s *RegisteredModel) Exists(ctx context.Context, w *databricks.WorkspaceCli
|
||||||
func (s *RegisteredModel) TerraformResourceName() string {
|
func (s *RegisteredModel) TerraformResourceName() string {
|
||||||
return "databricks_registered_model"
|
return "databricks_registered_model"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *RegisteredModel) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if s.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.URL = urlPrefix + "explore/data/models/" + strings.ReplaceAll(s.ID, ".", "/") + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RegisteredModel) GetName() string {
|
||||||
|
return s.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *RegisteredModel) GetURL() string {
|
||||||
|
return s.URL
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package resources
|
package resources
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/databricks/databricks-sdk-go"
|
||||||
"github.com/databricks/databricks-sdk-go/marshal"
|
"github.com/databricks/databricks-sdk-go/marshal"
|
||||||
"github.com/databricks/databricks-sdk-go/service/catalog"
|
"github.com/databricks/databricks-sdk-go/service/catalog"
|
||||||
)
|
)
|
||||||
|
@ -16,6 +21,30 @@ type Schema struct {
|
||||||
*catalog.CreateSchema
|
*catalog.CreateSchema
|
||||||
|
|
||||||
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
ModifiedStatus ModifiedStatus `json:"modified_status,omitempty" bundle:"internal"`
|
||||||
|
URL string `json:"url,omitempty" bundle:"internal"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) Exists(ctx context.Context, w *databricks.WorkspaceClient, id string) (bool, error) {
|
||||||
|
return false, fmt.Errorf("schema.Exists() is not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) TerraformResourceName() string {
|
||||||
|
return "databricks_schema"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) InitializeURL(urlPrefix string, urlSuffix string) {
|
||||||
|
if s.ID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.URL = urlPrefix + "explore/data/" + strings.ReplaceAll(s.ID, ".", "/") + urlSuffix
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) GetURL() string {
|
||||||
|
return s.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Schema) GetName() string {
|
||||||
|
return s.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Schema) UnmarshalJSON(b []byte) error {
|
func (s *Schema) UnmarshalJSON(b []byte) error {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package config
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -61,3 +62,22 @@ func TestCustomMarshallerIsImplemented(t *testing.T) {
|
||||||
}, "Resource %s does not have a custom unmarshaller", field.Name)
|
}, "Resource %s does not have a custom unmarshaller", field.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResourcesAllResourcesCompleteness(t *testing.T) {
|
||||||
|
r := Resources{}
|
||||||
|
rt := reflect.TypeOf(r)
|
||||||
|
|
||||||
|
result := r.AllResources()
|
||||||
|
|
||||||
|
for i := 0; i < rt.NumField(); i++ {
|
||||||
|
field := rt.Field(i)
|
||||||
|
jsonTag := field.Tag.Get("json")
|
||||||
|
|
||||||
|
if idx := strings.Index(jsonTag, ","); idx != -1 {
|
||||||
|
jsonTag = jsonTag[:idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
_, exists := result[jsonTag]
|
||||||
|
assert.True(t, exists, "Field %s is missing in AllResources map", field.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package render
|
package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
@ -56,7 +58,7 @@ const warningTemplate = `{{ "Warning" | yellow }}: {{ .Summary }}
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const summaryTemplate = `{{- if .Name -}}
|
const summaryHeaderTemplate = `{{- if .Name -}}
|
||||||
Name: {{ .Name | bold }}
|
Name: {{ .Name | bold }}
|
||||||
{{- if .Target }}
|
{{- if .Target }}
|
||||||
Target: {{ .Target | bold }}
|
Target: {{ .Target | bold }}
|
||||||
|
@ -73,12 +75,30 @@ Workspace:
|
||||||
Path: {{ .Path | bold }}
|
Path: {{ .Path | bold }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
{{- end }}
|
{{- end }}
|
||||||
|
{{ end -}}`
|
||||||
|
|
||||||
{{ end -}}
|
const resourcesTemplate = `Resources:
|
||||||
|
{{- range . }}
|
||||||
{{ .Trailer }}
|
{{ .GroupName }}:
|
||||||
|
{{- range .Resources }}
|
||||||
|
{{ .Key | bold }}:
|
||||||
|
Name: {{ .Name }}
|
||||||
|
URL: {{ if .URL }}{{ .URL | cyan }}{{ else }}{{ "(not deployed)" | cyan }}{{ end }}
|
||||||
|
{{- end }}
|
||||||
|
{{- end }}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
type ResourceGroup struct {
|
||||||
|
GroupName string
|
||||||
|
Resources []ResourceInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResourceInfo struct {
|
||||||
|
Key string
|
||||||
|
Name string
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
func pluralize(n int, singular, plural string) string {
|
func pluralize(n int, singular, plural string) string {
|
||||||
if n == 1 {
|
if n == 1 {
|
||||||
return fmt.Sprintf("%d %s", n, singular)
|
return fmt.Sprintf("%d %s", n, singular)
|
||||||
|
@ -95,15 +115,15 @@ func buildTrailer(diags diag.Diagnostics) string {
|
||||||
parts = append(parts, color.YellowString(pluralize(warnings, "warning", "warnings")))
|
parts = append(parts, color.YellowString(pluralize(warnings, "warning", "warnings")))
|
||||||
}
|
}
|
||||||
if len(parts) > 0 {
|
if len(parts) > 0 {
|
||||||
return fmt.Sprintf("Found %s", strings.Join(parts, " and "))
|
return fmt.Sprintf("Found %s\n", strings.Join(parts, " and "))
|
||||||
} else {
|
} else {
|
||||||
return color.GreenString("Validation OK!")
|
return color.GreenString("Validation OK!\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderSummaryTemplate(out io.Writer, b *bundle.Bundle, diags diag.Diagnostics) error {
|
func renderSummaryHeaderTemplate(out io.Writer, b *bundle.Bundle) error {
|
||||||
if b == nil {
|
if b == nil {
|
||||||
return renderSummaryTemplate(out, &bundle.Bundle{}, diags)
|
return renderSummaryHeaderTemplate(out, &bundle.Bundle{})
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentUser = &iam.User{}
|
var currentUser = &iam.User{}
|
||||||
|
@ -114,20 +134,19 @@ func renderSummaryTemplate(out io.Writer, b *bundle.Bundle, diags diag.Diagnosti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t := template.Must(template.New("summary").Funcs(renderFuncMap).Parse(summaryTemplate))
|
t := template.Must(template.New("summary").Funcs(renderFuncMap).Parse(summaryHeaderTemplate))
|
||||||
err := t.Execute(out, map[string]any{
|
err := t.Execute(out, map[string]any{
|
||||||
"Name": b.Config.Bundle.Name,
|
"Name": b.Config.Bundle.Name,
|
||||||
"Target": b.Config.Bundle.Target,
|
"Target": b.Config.Bundle.Target,
|
||||||
"User": currentUser.UserName,
|
"User": currentUser.UserName,
|
||||||
"Path": b.Config.Workspace.RootPath,
|
"Path": b.Config.Workspace.RootPath,
|
||||||
"Host": b.Config.Workspace.Host,
|
"Host": b.Config.Workspace.Host,
|
||||||
"Trailer": buildTrailer(diags),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderDiagnostics(out io.Writer, b *bundle.Bundle, diags diag.Diagnostics) error {
|
func renderDiagnosticsOnly(out io.Writer, b *bundle.Bundle, diags diag.Diagnostics) error {
|
||||||
errorT := template.Must(template.New("error").Funcs(renderFuncMap).Parse(errorTemplate))
|
errorT := template.Must(template.New("error").Funcs(renderFuncMap).Parse(errorTemplate))
|
||||||
warningT := template.Must(template.New("warning").Funcs(renderFuncMap).Parse(warningTemplate))
|
warningT := template.Must(template.New("warning").Funcs(renderFuncMap).Parse(warningTemplate))
|
||||||
|
|
||||||
|
@ -173,19 +192,74 @@ type RenderOptions struct {
|
||||||
RenderSummaryTable bool
|
RenderSummaryTable bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RenderTextOutput renders the diagnostics in a human-readable format.
|
// RenderDiagnostics renders the diagnostics in a human-readable format.
|
||||||
func RenderTextOutput(out io.Writer, b *bundle.Bundle, diags diag.Diagnostics, opts RenderOptions) error {
|
func RenderDiagnostics(out io.Writer, b *bundle.Bundle, diags diag.Diagnostics, opts RenderOptions) error {
|
||||||
err := renderDiagnostics(out, b, diags)
|
err := renderDiagnosticsOnly(out, b, diags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to render diagnostics: %w", err)
|
return fmt.Errorf("failed to render diagnostics: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.RenderSummaryTable {
|
if opts.RenderSummaryTable {
|
||||||
err = renderSummaryTemplate(out, b, diags)
|
if b != nil {
|
||||||
|
err = renderSummaryHeaderTemplate(out, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to render summary: %w", err)
|
return fmt.Errorf("failed to render summary: %w", err)
|
||||||
}
|
}
|
||||||
|
io.WriteString(out, "\n")
|
||||||
|
}
|
||||||
|
trailer := buildTrailer(diags)
|
||||||
|
io.WriteString(out, trailer)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RenderSummary(ctx context.Context, out io.Writer, b *bundle.Bundle) error {
|
||||||
|
if err := renderSummaryHeaderTemplate(out, b); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceGroups []ResourceGroup
|
||||||
|
|
||||||
|
for group, r := range b.Config.Resources.AllResources() {
|
||||||
|
resources := make([]ResourceInfo, 0, len(r))
|
||||||
|
for key, resource := range r {
|
||||||
|
resources = append(resources, ResourceInfo{
|
||||||
|
Key: key,
|
||||||
|
Name: resource.GetName(),
|
||||||
|
URL: resource.GetURL(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(resources) > 0 {
|
||||||
|
capitalizedGroup := strings.ToUpper(group[:1]) + group[1:]
|
||||||
|
resourceGroups = append(resourceGroups, ResourceGroup{
|
||||||
|
GroupName: capitalizedGroup,
|
||||||
|
Resources: resources,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := renderResourcesTemplate(out, resourceGroups); err != nil {
|
||||||
|
return fmt.Errorf("failed to render resources template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to sort and render resource groups using the template
|
||||||
|
func renderResourcesTemplate(out io.Writer, resourceGroups []ResourceGroup) error {
|
||||||
|
// Sort everything to ensure consistent output
|
||||||
|
sort.Slice(resourceGroups, func(i, j int) bool {
|
||||||
|
return resourceGroups[i].GroupName < resourceGroups[j].GroupName
|
||||||
|
})
|
||||||
|
for _, group := range resourceGroups {
|
||||||
|
sort.Slice(group.Resources, func(i, j int) bool {
|
||||||
|
return group.Resources[i].Key < group.Resources[j].Key
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
t := template.Must(template.New("resources").Funcs(renderFuncMap).Parse(resourcesTemplate))
|
||||||
|
|
||||||
|
return t.Execute(out, resourceGroups)
|
||||||
|
}
|
||||||
|
|
|
@ -2,14 +2,19 @@ package render
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/databricks/cli/bundle"
|
"github.com/databricks/cli/bundle"
|
||||||
"github.com/databricks/cli/bundle/config"
|
"github.com/databricks/cli/bundle/config"
|
||||||
|
"github.com/databricks/cli/bundle/config/resources"
|
||||||
"github.com/databricks/cli/libs/diag"
|
"github.com/databricks/cli/libs/diag"
|
||||||
"github.com/databricks/cli/libs/dyn"
|
"github.com/databricks/cli/libs/dyn"
|
||||||
assert "github.com/databricks/cli/libs/dyn/dynassert"
|
"github.com/databricks/databricks-sdk-go/service/catalog"
|
||||||
"github.com/databricks/databricks-sdk-go/service/iam"
|
"github.com/databricks/databricks-sdk-go/service/iam"
|
||||||
|
"github.com/databricks/databricks-sdk-go/service/jobs"
|
||||||
|
"github.com/databricks/databricks-sdk-go/service/pipelines"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -192,10 +197,10 @@ func TestRenderTextOutput(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
writer := &bytes.Buffer{}
|
writer := &bytes.Buffer{}
|
||||||
|
|
||||||
err := RenderTextOutput(writer, tc.bundle, tc.diags, tc.opts)
|
err := RenderDiagnostics(writer, tc.bundle, tc.diags, tc.opts)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, tc.expected, writer.String())
|
require.Equal(t, tc.expected, writer.String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,10 +315,10 @@ func TestRenderDiagnostics(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
writer := &bytes.Buffer{}
|
writer := &bytes.Buffer{}
|
||||||
|
|
||||||
err := renderDiagnostics(writer, bundle, tc.diags)
|
err := renderDiagnosticsOnly(writer, bundle, tc.diags)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, tc.expected, writer.String())
|
require.Equal(t, tc.expected, writer.String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,8 +326,92 @@ func TestRenderDiagnostics(t *testing.T) {
|
||||||
func TestRenderSummaryTemplate_nilBundle(t *testing.T) {
|
func TestRenderSummaryTemplate_nilBundle(t *testing.T) {
|
||||||
writer := &bytes.Buffer{}
|
writer := &bytes.Buffer{}
|
||||||
|
|
||||||
err := renderSummaryTemplate(writer, nil, nil)
|
err := renderSummaryHeaderTemplate(writer, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "Validation OK!\n", writer.String())
|
io.WriteString(writer, buildTrailer(nil))
|
||||||
|
|
||||||
|
require.Equal(t, "Validation OK!\n", writer.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderSummary(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Create a mock bundle with various resources
|
||||||
|
b := &bundle.Bundle{
|
||||||
|
Config: config.Root{
|
||||||
|
Bundle: config.Bundle{
|
||||||
|
Name: "test-bundle",
|
||||||
|
Target: "test-target",
|
||||||
|
},
|
||||||
|
Workspace: config.Workspace{
|
||||||
|
Host: "https://mycompany.databricks.com/",
|
||||||
|
},
|
||||||
|
Resources: config.Resources{
|
||||||
|
Jobs: map[string]*resources.Job{
|
||||||
|
"job1": {
|
||||||
|
ID: "1",
|
||||||
|
URL: "https://url1",
|
||||||
|
JobSettings: &jobs.JobSettings{Name: "job1-name"},
|
||||||
|
},
|
||||||
|
"job2": {
|
||||||
|
ID: "2",
|
||||||
|
URL: "https://url2",
|
||||||
|
JobSettings: &jobs.JobSettings{Name: "job2-name"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Pipelines: map[string]*resources.Pipeline{
|
||||||
|
"pipeline2": {
|
||||||
|
ID: "4",
|
||||||
|
// no URL
|
||||||
|
PipelineSpec: &pipelines.PipelineSpec{Name: "pipeline2-name"},
|
||||||
|
},
|
||||||
|
"pipeline1": {
|
||||||
|
ID: "3",
|
||||||
|
URL: "https://url3",
|
||||||
|
PipelineSpec: &pipelines.PipelineSpec{Name: "pipeline1-name"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Schemas: map[string]*resources.Schema{
|
||||||
|
"schema1": {
|
||||||
|
ID: "catalog.schema",
|
||||||
|
CreateSchema: &catalog.CreateSchema{
|
||||||
|
Name: "schema",
|
||||||
|
},
|
||||||
|
// no URL
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
writer := &bytes.Buffer{}
|
||||||
|
err := RenderSummary(ctx, writer, b)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expectedSummary := `Name: test-bundle
|
||||||
|
Target: test-target
|
||||||
|
Workspace:
|
||||||
|
Host: https://mycompany.databricks.com/
|
||||||
|
Resources:
|
||||||
|
Jobs:
|
||||||
|
job1:
|
||||||
|
Name: job1-name
|
||||||
|
URL: https://url1
|
||||||
|
job2:
|
||||||
|
Name: job2-name
|
||||||
|
URL: https://url2
|
||||||
|
Pipelines:
|
||||||
|
pipeline1:
|
||||||
|
Name: pipeline1-name
|
||||||
|
URL: https://url3
|
||||||
|
pipeline2:
|
||||||
|
Name: pipeline2-name
|
||||||
|
URL: (not deployed)
|
||||||
|
Schemas:
|
||||||
|
schema1:
|
||||||
|
Name: schema
|
||||||
|
URL: (not deployed)
|
||||||
|
`
|
||||||
|
require.Equal(t, expectedSummary, writer.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func newDeployCommand() *cobra.Command {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderOpts := render.RenderOptions{RenderSummaryTable: false}
|
renderOpts := render.RenderOptions{RenderSummaryTable: false}
|
||||||
err := render.RenderTextOutput(cmd.OutOrStdout(), b, diags, renderOpts)
|
err := render.RenderDiagnostics(cmd.OutOrStdout(), b, diags, renderOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to render output: %w", err)
|
return fmt.Errorf("failed to render output: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,10 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/databricks/cli/bundle"
|
"github.com/databricks/cli/bundle"
|
||||||
|
"github.com/databricks/cli/bundle/config/mutator"
|
||||||
"github.com/databricks/cli/bundle/deploy/terraform"
|
"github.com/databricks/cli/bundle/deploy/terraform"
|
||||||
"github.com/databricks/cli/bundle/phases"
|
"github.com/databricks/cli/bundle/phases"
|
||||||
|
"github.com/databricks/cli/bundle/render"
|
||||||
"github.com/databricks/cli/cmd/bundle/utils"
|
"github.com/databricks/cli/cmd/bundle/utils"
|
||||||
"github.com/databricks/cli/cmd/root"
|
"github.com/databricks/cli/cmd/root"
|
||||||
"github.com/databricks/cli/libs/flags"
|
"github.com/databricks/cli/libs/flags"
|
||||||
|
@ -19,11 +21,8 @@ import (
|
||||||
func newSummaryCommand() *cobra.Command {
|
func newSummaryCommand() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "summary",
|
Use: "summary",
|
||||||
Short: "Describe the bundle resources and their deployment states",
|
Short: "Summarize resources deployed by this bundle",
|
||||||
Args: root.NoArgs,
|
Args: root.NoArgs,
|
||||||
|
|
||||||
// This command is currently intended for the Databricks VSCode extension only
|
|
||||||
Hidden: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var forcePull bool
|
var forcePull bool
|
||||||
|
@ -60,14 +59,15 @@ func newSummaryCommand() *cobra.Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
diags = bundle.Apply(ctx, b, terraform.Load())
|
diags = bundle.Apply(ctx, b,
|
||||||
|
bundle.Seq(terraform.Load(), mutator.InitializeURLs()))
|
||||||
if err := diags.Error(); err != nil {
|
if err := diags.Error(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch root.OutputType(cmd) {
|
switch root.OutputType(cmd) {
|
||||||
case flags.OutputText:
|
case flags.OutputText:
|
||||||
return fmt.Errorf("%w, only json output is supported", errors.ErrUnsupported)
|
return render.RenderSummary(ctx, cmd.OutOrStdout(), b)
|
||||||
case flags.OutputJSON:
|
case flags.OutputJSON:
|
||||||
buf, err := json.MarshalIndent(b.Config, "", " ")
|
buf, err := json.MarshalIndent(b.Config, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -54,7 +54,7 @@ func newValidateCommand() *cobra.Command {
|
||||||
switch root.OutputType(cmd) {
|
switch root.OutputType(cmd) {
|
||||||
case flags.OutputText:
|
case flags.OutputText:
|
||||||
renderOpts := render.RenderOptions{RenderSummaryTable: true}
|
renderOpts := render.RenderOptions{RenderSummaryTable: true}
|
||||||
err := render.RenderTextOutput(cmd.OutOrStdout(), b, diags, renderOpts)
|
err := render.RenderDiagnostics(cmd.OutOrStdout(), b, diags, renderOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to render output: %w", err)
|
return fmt.Errorf("failed to render output: %w", err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue