2023-07-21 08:59:02 +00:00
package template
import (
2023-08-25 09:03:42 +00:00
"context"
"errors"
2023-07-21 08:59:02 +00:00
"fmt"
2024-06-06 07:11:23 +00:00
"math/rand"
2023-07-27 09:51:31 +00:00
"net/url"
2023-10-19 07:08:36 +00:00
"os"
2023-07-25 14:42:53 +00:00
"regexp"
2023-07-21 08:59:02 +00:00
"text/template"
2023-08-25 09:03:42 +00:00
"github.com/databricks/cli/cmd/root"
2024-10-10 13:02:25 +00:00
"github.com/databricks/cli/libs/iamutil"
2024-02-19 09:15:17 +00:00
"github.com/databricks/databricks-sdk-go/apierr"
2023-08-25 09:03:42 +00:00
"github.com/databricks/databricks-sdk-go/service/iam"
2024-07-19 11:38:20 +00:00
"github.com/google/uuid"
2023-07-21 08:59:02 +00:00
)
type ErrFail struct {
msg string
}
func ( err ErrFail ) Error ( ) string {
return err . msg
}
2023-08-15 16:07:22 +00:00
type pair struct {
k string
v any
}
2023-09-06 09:52:31 +00:00
var cachedUser * iam . User
var cachedIsServicePrincipal * bool
2024-02-19 09:15:17 +00:00
var cachedCatalog * string
2023-09-06 09:52:31 +00:00
2024-12-02 10:29:29 +00:00
// UUID that is stable for the duration of the template execution. This can be used
// to populate the `bundle.uuid` field in databricks.yml by template authors.
//
// It's automatically logged in our telemetry logs when `databricks bundle init`
// is run and can be used to attribute DBU revenue to bundle templates.
var bundleUuid = uuid . New ( ) . String ( )
2023-08-25 09:03:42 +00:00
func loadHelpers ( ctx context . Context ) template . FuncMap {
w := root . WorkspaceClient ( ctx )
return template . FuncMap {
"fail" : func ( format string , args ... any ) ( any , error ) {
return nil , ErrFail { fmt . Sprintf ( format , args ... ) }
} ,
// Alias for https://pkg.go.dev/net/url#Parse. Allows usage of all methods of url.URL
"url" : func ( rawUrl string ) ( * url . URL , error ) {
return url . Parse ( rawUrl )
} ,
// Alias for https://pkg.go.dev/regexp#Compile. Allows usage of all methods of regexp.Regexp
"regexp" : func ( expr string ) ( * regexp . Regexp , error ) {
return regexp . Compile ( expr )
} ,
2024-06-06 07:11:23 +00:00
// Alias for https://pkg.go.dev/math/rand#Intn. Returns, as an int, a non-negative pseudo-random number in the half-open interval [0,n).
"random_int" : func ( n int ) int {
return rand . Intn ( n )
} ,
2024-07-19 11:38:20 +00:00
// Alias for https://pkg.go.dev/github.com/google/uuid#New. Returns, as a string, a UUID which is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC 4122.
"uuid" : func ( ) string {
return uuid . New ( ) . String ( )
} ,
2024-12-02 10:29:29 +00:00
"bundle_uuid" : func ( ) string {
return bundleUuid
} ,
2023-08-25 09:03:42 +00:00
// A key value pair. This is used with the map function to generate maps
// to use inside a template
"pair" : func ( k string , v any ) pair {
return pair { k , v }
} ,
// map converts a list of pairs to a map object. This is useful to pass multiple
// objects to templates defined in the library directory. Go text template
// syntax for invoking a template only allows specifying a single argument,
// this function can be used to workaround that limitation.
//
// For example: {{template "my_template" (map (pair "foo" $arg1) (pair "bar" $arg2))}}
// $arg1 and $arg2 can be referred from inside "my_template" as ".foo" and ".bar"
"map" : func ( pairs ... pair ) map [ string ] any {
result := make ( map [ string ] any , 0 )
for _ , p := range pairs {
result [ p . k ] = p . v
}
return result
} ,
// Get smallest node type (follows Terraform's GetSmallestNodeType)
"smallest_node_type" : func ( ) ( string , error ) {
if w . Config . Host == "" {
2023-10-19 07:08:36 +00:00
return "" , errors . New ( "cannot determine target workspace, please first setup a configuration profile using 'databricks configure'" )
2023-08-25 09:03:42 +00:00
}
if w . Config . IsAzure ( ) {
return "Standard_D3_v2" , nil
} else if w . Config . IsGcp ( ) {
return "n1-standard-4" , nil
}
return "i3.xlarge" , nil
} ,
2023-10-19 07:08:36 +00:00
"path_separator" : func ( ) string {
return string ( os . PathSeparator )
} ,
2023-08-25 09:03:42 +00:00
"workspace_host" : func ( ) ( string , error ) {
if w . Config . Host == "" {
2023-10-19 07:08:36 +00:00
return "" , errors . New ( "cannot determine target workspace, please first setup a configuration profile using 'databricks configure'" )
2023-08-25 09:03:42 +00:00
}
return w . Config . Host , nil
} ,
"user_name" : func ( ) ( string , error ) {
2023-09-06 09:52:31 +00:00
if cachedUser == nil {
2023-08-25 09:03:42 +00:00
var err error
2023-09-06 09:52:31 +00:00
cachedUser , err = w . CurrentUser . Me ( ctx )
2023-08-25 09:03:42 +00:00
if err != nil {
return "" , err
}
}
2023-09-06 09:52:31 +00:00
result := cachedUser . UserName
2023-08-25 09:03:42 +00:00
if result == "" {
2023-09-06 09:52:31 +00:00
result = cachedUser . Id
2023-08-25 09:03:42 +00:00
}
return result , nil
} ,
2024-02-01 16:46:07 +00:00
"short_name" : func ( ) ( string , error ) {
if cachedUser == nil {
var err error
cachedUser , err = w . CurrentUser . Me ( ctx )
if err != nil {
return "" , err
}
}
2024-10-10 13:02:25 +00:00
return iamutil . GetShortUserName ( cachedUser ) , nil
2024-02-01 16:46:07 +00:00
} ,
2024-02-19 09:15:17 +00:00
// Get the default workspace catalog. If there is no default, or if
// Unity Catalog is not enabled, return an empty string.
"default_catalog" : func ( ) ( string , error ) {
if cachedCatalog == nil {
metastore , err := w . Metastores . Current ( ctx )
if err != nil {
var aerr * apierr . APIError
2024-10-11 12:28:56 +00:00
if errors . As ( err , & aerr ) && ( aerr . ErrorCode == "PERMISSION_DENIED" || aerr . ErrorCode == "METASTORE_DOES_NOT_EXIST" ) {
// Ignore: access denied or workspace doesn't have a metastore assigned
2024-02-19 09:15:17 +00:00
empty_default := ""
cachedCatalog = & empty_default
return "" , nil
}
return "" , err
}
cachedCatalog = & metastore . DefaultCatalogName
}
return * cachedCatalog , nil
} ,
2023-08-25 09:03:42 +00:00
"is_service_principal" : func ( ) ( bool , error ) {
2023-09-06 09:52:31 +00:00
if cachedIsServicePrincipal != nil {
return * cachedIsServicePrincipal , nil
2023-08-25 09:03:42 +00:00
}
2023-09-06 09:52:31 +00:00
if cachedUser == nil {
2023-08-25 09:03:42 +00:00
var err error
2023-09-06 09:52:31 +00:00
cachedUser , err = w . CurrentUser . Me ( ctx )
2023-08-25 09:03:42 +00:00
if err != nil {
return false , err
}
}
2024-10-10 13:02:25 +00:00
result := iamutil . IsServicePrincipal ( cachedUser )
2023-09-06 09:52:31 +00:00
cachedIsServicePrincipal = & result
2023-08-25 09:03:42 +00:00
return result , nil
} ,
}
2023-07-21 08:59:02 +00:00
}