mirror of https://github.com/databricks/cli.git
add support for library
This commit is contained in:
parent
95391f792c
commit
2486a96cd7
|
@ -0,0 +1,6 @@
|
|||
{{define "email"}}shreyas.goenka@databricks.com{{end}}
|
||||
{{define "get_host"}}
|
||||
{{ with urlParse . }}{{-
|
||||
print .Scheme `://` .Host
|
||||
-}}{{end}}
|
||||
{{end}}
|
|
@ -2,3 +2,6 @@ This file should only be generated for Azure
|
|||
{{if ne .cloud_type "Azure"}}
|
||||
{{skipThisFile}}
|
||||
{{end}}
|
||||
|
||||
{{ template "email" }}
|
||||
{{ template "get_host" "https://adb-xxxx.xx.azuredatabricks.net/sql/queries" }}
|
||||
|
|
|
@ -2,6 +2,7 @@ package template
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -9,39 +10,63 @@ import (
|
|||
"text/template"
|
||||
)
|
||||
|
||||
// Executes the template by applying config on it. Returns the materialized config
|
||||
type renderer struct {
|
||||
config map[string]any
|
||||
|
||||
baseTemplate *template.Template
|
||||
}
|
||||
|
||||
func newRenderer(config map[string]any, libraryRoot string) (*renderer, error) {
|
||||
tmpl, err := template.New("").Funcs(HelperFuncs).ParseGlob(filepath.Join(libraryRoot, "*"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &renderer{
|
||||
config: config,
|
||||
baseTemplate: tmpl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Executes the template by applying config on it. Returns the materialized template
|
||||
// as a string
|
||||
func executeTemplate(config map[string]any, templateDefinition string) (string, error) {
|
||||
// configure template with helper functions
|
||||
tmpl, err := template.New("").Funcs(HelperFuncs).Parse(templateDefinition)
|
||||
func (r *renderer) executeTemplate(templateDefinition string) (string, error) {
|
||||
// Create copy of base template so as to not overwrite it
|
||||
tmpl, err := r.baseTemplate.Clone()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// execute template
|
||||
// Parse the template text
|
||||
tmpl, err = tmpl.Parse(templateDefinition)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Execute template and get result
|
||||
result := strings.Builder{}
|
||||
err = tmpl.Execute(&result, config)
|
||||
err = tmpl.Execute(&result, r.config)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result.String(), nil
|
||||
}
|
||||
|
||||
func generateFile(config map[string]any, pathTemplate, contentTemplate string, perm fs.FileMode) error {
|
||||
func (r *renderer) generateFile(pathTemplate, contentTemplate string, perm fs.FileMode) error {
|
||||
// compute file content
|
||||
fileContent, err := executeTemplate(config, contentTemplate)
|
||||
fileContent, err := r.executeTemplate(contentTemplate)
|
||||
if errors.Is(err, errSkipThisFile) {
|
||||
// skip this file
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to compute file content for %s. %w", pathTemplate, err)
|
||||
}
|
||||
|
||||
// compute the path for this file
|
||||
path, err := executeTemplate(config, pathTemplate)
|
||||
path, err := r.executeTemplate(pathTemplate)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to compute path for %s. %w", pathTemplate, err)
|
||||
}
|
||||
// create any intermediate directories required. Directories are lazily generated
|
||||
// only when they are required for a file.
|
||||
|
@ -55,7 +80,7 @@ func generateFile(config map[string]any, pathTemplate, contentTemplate string, p
|
|||
}
|
||||
|
||||
// TODO: use local filer client for this function. https://github.com/databricks/cli/issues/511
|
||||
func walkFileTree(config map[string]any, templateRoot, instanceRoot string) error {
|
||||
func walkFileTree(r *renderer, templateRoot, instanceRoot string) error {
|
||||
return filepath.WalkDir(templateRoot, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -87,6 +112,6 @@ func walkFileTree(config map[string]any, templateRoot, instanceRoot string) erro
|
|||
return err
|
||||
}
|
||||
|
||||
return generateFile(config, filepath.Join(instanceRoot, relPathTemplate), contentTemplate, info.Mode().Perm())
|
||||
return r.generateFile(filepath.Join(instanceRoot, relPathTemplate), contentTemplate, info.Mode().Perm())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -17,22 +18,40 @@ Sheep wool is the best!
|
|||
{{else}}
|
||||
{{.Animal}} wool is not too bad...
|
||||
{{end}}
|
||||
My email is {{template "email"}}
|
||||
`
|
||||
statement, err := executeTemplate(map[string]any{
|
||||
|
||||
r := renderer{
|
||||
config: map[string]any{
|
||||
"Material": "wool",
|
||||
"count": 1,
|
||||
"Animal": "sheep",
|
||||
}, templateText)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "\"1 items are made of wool\".\n\nSheep wool is the best!\n\n", statement)
|
||||
},
|
||||
baseTemplate: template.Must(template.New("base").Parse(`{{define "email"}}shreyas.goenka@databricks.com{{end}}`)),
|
||||
}
|
||||
|
||||
statement, err = executeTemplate(map[string]any{
|
||||
statement, err := r.executeTemplate(templateText)
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, statement, `"1 items are made of wool"`)
|
||||
assert.NotContains(t, statement, `cat wool is not too bad.."`)
|
||||
assert.Contains(t, statement, "Sheep wool is the best!")
|
||||
assert.Contains(t, statement, `My email is shreyas.goenka@databricks.com`)
|
||||
|
||||
r = renderer{
|
||||
config: map[string]any{
|
||||
"Material": "wool",
|
||||
"count": 1,
|
||||
"Animal": "cat",
|
||||
}, templateText)
|
||||
},
|
||||
baseTemplate: template.Must(template.New("base").Parse(`{{define "email"}}hrithik.roshan@databricks.com{{end}}`)),
|
||||
}
|
||||
|
||||
statement, err = r.executeTemplate(templateText)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "\"1 items are made of wool\".\n\ncat wool is not too bad...\n\n", statement)
|
||||
assert.Contains(t, statement, `"1 items are made of wool"`)
|
||||
assert.Contains(t, statement, `cat wool is not too bad...`)
|
||||
assert.NotContains(t, statement, "Sheep wool is the best!")
|
||||
assert.Contains(t, statement, `My email is hrithik.roshan@databricks.com`)
|
||||
}
|
||||
|
||||
func TestGenerateFile(t *testing.T) {
|
||||
|
@ -46,11 +65,16 @@ func TestGenerateFile(t *testing.T) {
|
|||
{{.Animal}} wool is not too bad...
|
||||
{{end}}
|
||||
`
|
||||
err := generateFile(map[string]any{
|
||||
|
||||
r := renderer{
|
||||
config: map[string]any{
|
||||
"Material": "wool",
|
||||
"count": 1,
|
||||
"Animal": "cat",
|
||||
}, pathTemplate, contentTemplate, 0444)
|
||||
},
|
||||
baseTemplate: template.New("base"),
|
||||
}
|
||||
err := r.generateFile(pathTemplate, contentTemplate, 0444)
|
||||
require.NoError(t, err)
|
||||
|
||||
// assert file exists
|
||||
|
|
|
@ -2,6 +2,7 @@ package template
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
|
@ -13,4 +14,7 @@ var HelperFuncs = template.FuncMap{
|
|||
"skipThisFile": func() (any, error) {
|
||||
return nil, errSkipThisFile
|
||||
},
|
||||
"urlParse": func(rawUrl string) (*url.URL, error) {
|
||||
return url.Parse(rawUrl)
|
||||
},
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
const schemaFileName = "databricks_template_schema.json"
|
||||
const templateDirName = "template"
|
||||
const libraryDirName = "library"
|
||||
|
||||
func Materialize(templateRoot, instanceRoot, configPath string) error {
|
||||
// read the file containing schema for template input parameters
|
||||
|
@ -14,12 +15,17 @@ func Materialize(templateRoot, instanceRoot, configPath string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// read user config to initalize the template with
|
||||
// read user config to initialize the template with
|
||||
config, err := schema.ReadConfig(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := newRenderer(config, filepath.Join(templateRoot, libraryDirName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// materialize the template
|
||||
return walkFileTree(config, filepath.Join(templateRoot, templateDirName), instanceRoot)
|
||||
return walkFileTree(r, filepath.Join(templateRoot, templateDirName), instanceRoot)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue