2022-05-14 17:55:00 +00:00
|
|
|
|
package project
|
|
|
|
|
|
|
|
|
|
import (
|
2022-09-14 13:08:55 +00:00
|
|
|
|
"errors"
|
2022-05-14 17:55:00 +00:00
|
|
|
|
"fmt"
|
2022-09-07 12:30:10 +00:00
|
|
|
|
"io"
|
2022-05-14 17:55:00 +00:00
|
|
|
|
"os"
|
2022-09-16 09:06:58 +00:00
|
|
|
|
"path/filepath"
|
2022-05-14 17:55:00 +00:00
|
|
|
|
"reflect"
|
|
|
|
|
|
2022-07-07 18:56:59 +00:00
|
|
|
|
"github.com/databricks/bricks/folders"
|
2022-09-07 09:55:59 +00:00
|
|
|
|
"github.com/databricks/databricks-sdk-go/service/clusters"
|
|
|
|
|
|
2022-05-14 17:55:00 +00:00
|
|
|
|
"github.com/ghodss/yaml"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Isolation string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
None Isolation = ""
|
|
|
|
|
Soft Isolation = "soft"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// ConfigFile is the name of project configuration file
|
|
|
|
|
const ConfigFile = "databricks.yml"
|
|
|
|
|
|
|
|
|
|
type Assertions struct {
|
|
|
|
|
Groups []string `json:"groups,omitempty"`
|
|
|
|
|
Secrets []string `json:"secrets,omitempty"`
|
|
|
|
|
ServicePrincipals []string `json:"service_principals,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 09:06:58 +00:00
|
|
|
|
type Config struct {
|
2022-10-19 14:22:55 +00:00
|
|
|
|
Name string `json:"name"` // or do default from folder name?..
|
2022-05-14 17:55:00 +00:00
|
|
|
|
Isolation Isolation `json:"isolation,omitempty"`
|
|
|
|
|
|
2022-05-20 19:40:03 +00:00
|
|
|
|
// development-time vs deployment-time resources
|
2022-09-07 09:55:59 +00:00
|
|
|
|
DevCluster *clusters.ClusterInfo `json:"dev_cluster,omitempty"`
|
2022-05-14 17:55:00 +00:00
|
|
|
|
|
|
|
|
|
// Assertions defines a list of configurations expected to be applied
|
|
|
|
|
// to the workspace by a higher-privileged user (or service principal)
|
|
|
|
|
// in order for the deploy command to work, as individual project teams
|
|
|
|
|
// in almost all the cases don’t have admin privileges on Databricks
|
|
|
|
|
// workspaces.
|
|
|
|
|
//
|
|
|
|
|
// This configuration simplifies the flexibility of individual project
|
|
|
|
|
// teams, make jobs deployment easier and portable across environments.
|
|
|
|
|
// This configuration block would contain the following entities to be
|
|
|
|
|
// created by administrator users or admin-level automation, like Terraform
|
|
|
|
|
// and/or SCIM provisioning.
|
|
|
|
|
Assertions *Assertions `json:"assertions,omitempty"`
|
2022-09-22 11:40:11 +00:00
|
|
|
|
|
|
|
|
|
// Environments contain this project's defined environments.
|
|
|
|
|
// They can be used to differentiate settings and resources between
|
|
|
|
|
// development, staging, production, etc.
|
|
|
|
|
// If not specified, the code below initializes this field with a
|
|
|
|
|
// single default-initialized environment called "development".
|
|
|
|
|
Environments map[string]Environment `json:"environments"`
|
2022-05-14 17:55:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 09:06:58 +00:00
|
|
|
|
func (c Config) IsDevClusterDefined() bool {
|
|
|
|
|
return reflect.ValueOf(c.DevCluster).IsZero()
|
2022-05-14 17:55:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsDevClusterJustReference denotes reference-only clusters.
|
|
|
|
|
// This conflicts with Soft isolation. Happens for cost-restricted projects,
|
|
|
|
|
// where there's only a single Shared Autoscaling cluster per workspace and
|
|
|
|
|
// general users have no ability to create other iteractive clusters.
|
2022-09-16 09:06:58 +00:00
|
|
|
|
func (c *Config) IsDevClusterJustReference() bool {
|
|
|
|
|
if c.DevCluster.ClusterName == "" {
|
2022-05-14 17:55:00 +00:00
|
|
|
|
return false
|
|
|
|
|
}
|
2022-09-16 09:06:58 +00:00
|
|
|
|
return reflect.DeepEqual(c.DevCluster, &clusters.ClusterInfo{
|
|
|
|
|
ClusterName: c.DevCluster.ClusterName,
|
2022-05-14 17:55:00 +00:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IsDatabricksProject returns true for folders with `databricks.yml`
|
|
|
|
|
// in the parent tree
|
|
|
|
|
func IsDatabricksProject() bool {
|
|
|
|
|
_, err := findProjectRoot()
|
|
|
|
|
return err == nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 09:06:58 +00:00
|
|
|
|
func loadProjectConf(root string) (c Config, err error) {
|
2022-09-21 11:55:37 +00:00
|
|
|
|
configFilePath := filepath.Join(root, ConfigFile)
|
|
|
|
|
|
|
|
|
|
if _, err := os.Stat(configFilePath); errors.Is(err, os.ErrNotExist) {
|
|
|
|
|
baseDir := filepath.Base(root)
|
|
|
|
|
// If bricks config file is missing we assume the project root dir name
|
|
|
|
|
// as the name of the project
|
2022-09-22 11:40:11 +00:00
|
|
|
|
return validateAndApplyProjectDefaults(Config{Name: baseDir})
|
2022-09-21 11:55:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
config, err := os.Open(configFilePath)
|
2022-05-14 17:55:00 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-09-07 12:30:10 +00:00
|
|
|
|
raw, err := io.ReadAll(config)
|
2022-05-14 17:55:00 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-09-16 09:06:58 +00:00
|
|
|
|
err = yaml.Unmarshal(raw, &c)
|
2022-05-14 17:55:00 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2022-09-16 09:06:58 +00:00
|
|
|
|
return validateAndApplyProjectDefaults(c)
|
2022-05-14 17:55:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-09-16 09:06:58 +00:00
|
|
|
|
func validateAndApplyProjectDefaults(c Config) (Config, error) {
|
2022-09-22 11:40:11 +00:00
|
|
|
|
// If no environments are specified, define default environment under default name.
|
|
|
|
|
if c.Environments == nil {
|
|
|
|
|
c.Environments = make(map[string]Environment)
|
|
|
|
|
c.Environments[DefaultEnvironment] = Environment{}
|
|
|
|
|
}
|
2022-09-07 09:55:59 +00:00
|
|
|
|
// defaultCluster := clusters.ClusterInfo{
|
2022-05-14 17:55:00 +00:00
|
|
|
|
// NodeTypeID: "smallest",
|
|
|
|
|
// SparkVersion: "latest",
|
|
|
|
|
// AutoterminationMinutes: 30,
|
|
|
|
|
// }
|
2022-09-16 09:06:58 +00:00
|
|
|
|
return c, nil
|
2022-05-14 17:55:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func findProjectRoot() (string, error) {
|
2022-09-14 13:08:55 +00:00
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
dir, err := folders.FindDirWithLeaf(wd, ConfigFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
|
|
|
return "", fmt.Errorf("cannot find %s anywhere", ConfigFile)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dir, nil
|
2022-05-14 17:55:00 +00:00
|
|
|
|
}
|