diff --git a/integration/resources/good-postgres-with-template.yml b/integration/resources/good-postgres-with-template.yml new file mode 100644 index 0000000..06f60cb --- /dev/null +++ b/integration/resources/good-postgres-with-template.yml @@ -0,0 +1,18 @@ +:targets: + - :name: "My Postgres database 1" + :type: postgres + :host: {{.host}} + :database: sql_runner_tests_1 + :port: 5432 + :username: {{.username}} + :password: {{.password}} + :ssl: false # SSL disabled by default +:variables: + :test_schema: sql_runner_tests + :timeFormat: "2006_01_02" +:steps: + - :name: Create schema and table + :queries: + - :name: Create schema and table + :file: postgres-sql/good/1.sql + :template: true diff --git a/sql_runner/consul_provider.go b/sql_runner/consul_provider.go index d2cf9cb..271dd7a 100644 --- a/sql_runner/consul_provider.go +++ b/sql_runner/consul_provider.go @@ -15,12 +15,14 @@ package main type ConsulPlaybookProvider struct { consulAddress string consulKey string + variables map[string]string } -func NewConsulPlaybookProvider(consulAddress, consulKey string) *ConsulPlaybookProvider { +func NewConsulPlaybookProvider(consulAddress, consulKey string, variables map[string]string) *ConsulPlaybookProvider { return &ConsulPlaybookProvider{ consulAddress: consulAddress, consulKey: consulKey, + variables: variables, } } @@ -30,6 +32,6 @@ func (p ConsulPlaybookProvider) GetPlaybook() (*Playbook, error) { return nil, err } - playbook, pbErr := parsePlaybookYaml(lines) + playbook, pbErr := parsePlaybookYaml(lines, p.variables) return &playbook, pbErr } diff --git a/sql_runner/main.go b/sql_runner/main.go index 6921a3e..6a8bb5e 100644 --- a/sql_runner/main.go +++ b/sql_runner/main.go @@ -154,9 +154,9 @@ func processFlags() Options { // based on flags passed in func PlaybookProviderFromOptions(options Options) (PlaybookProvider, error) { if options.consul != "" { - return NewConsulPlaybookProvider(options.consul, options.playbook), nil + return NewConsulPlaybookProvider(options.consul, options.playbook, options.variables), nil } else if options.playbook != "" { - return NewYAMLFilePlaybookProvider(options.playbook), nil + return NewYAMLFilePlaybookProvider(options.playbook, options.variables), nil } else { return nil, errors.New("Cannot determine provider for playbook") } diff --git a/sql_runner/yaml_provider.go b/sql_runner/yaml_provider.go index 9ed5b17..f4f2054 100644 --- a/sql_runner/yaml_provider.go +++ b/sql_runner/yaml_provider.go @@ -14,11 +14,13 @@ package main type YAMLFilePlaybookProvider struct { playbookPath string + variables map[string]string } -func NewYAMLFilePlaybookProvider(playbookPath string) *YAMLFilePlaybookProvider { +func NewYAMLFilePlaybookProvider(playbookPath string, variables map[string]string) *YAMLFilePlaybookProvider { return &YAMLFilePlaybookProvider{ playbookPath: playbookPath, + variables: variables, } } @@ -28,6 +30,6 @@ func (p YAMLFilePlaybookProvider) GetPlaybook() (*Playbook, error) { return nil, err } - playbook, pbErr := parsePlaybookYaml(lines) + playbook, pbErr := parsePlaybookYaml(lines, p.variables) return &playbook, pbErr } diff --git a/sql_runner/yaml_utils.go b/sql_runner/yaml_utils.go index 739d20e..46f391d 100644 --- a/sql_runner/yaml_utils.go +++ b/sql_runner/yaml_utils.go @@ -17,6 +17,7 @@ import ( "gopkg.in/yaml.v1" "regexp" "strings" + "text/template" ) var ( @@ -26,13 +27,18 @@ var ( // Parses a playbook.yml to return the targets // to execute against and the steps to execute -func parsePlaybookYaml(playbookBytes []byte) (Playbook, error) { +func parsePlaybookYaml(playbookBytes []byte, variables map[string]string) (Playbook, error) { // Define and initialize the Playbook struct var playbook Playbook = NewPlaybook() // Clean up the YAML cleaned := cleanYaml(playbookBytes) - err := yaml.Unmarshal(cleaned, &playbook) + + // Run the yaml through the template engine + str, err := fillPlaybookTemplate(string(cleaned[:]), variables) + + // Unmarshal the yaml into the playbook + err = yaml.Unmarshal([]byte(str), &playbook) return playbook, err } @@ -51,3 +57,17 @@ func cleanYaml(rawYaml []byte) []byte { } return buffer.Bytes() } + +func fillPlaybookTemplate(playbookStr string, variables map[string]string) (string, error) { + t, err := template.New("playbook").Parse(playbookStr) + if err != nil { + return "", err + } + + var filled bytes.Buffer + if err := t.Execute(&filled, variables); err != nil { + return "", err + } + + return filled.String(), err +} diff --git a/sql_runner/yaml_utils_test.go b/sql_runner/yaml_utils_test.go index a6910a2..bece366 100644 --- a/sql_runner/yaml_utils_test.go +++ b/sql_runner/yaml_utils_test.go @@ -20,7 +20,7 @@ import ( func TestParsePlaybookYaml(t *testing.T) { assert := assert.New(t) - playbook, err := parsePlaybookYaml(nil) + playbook, err := parsePlaybookYaml(nil, nil) assert.Nil(err) assert.NotNil(playbook) assert.Equal(0, len(playbook.Targets)) @@ -30,7 +30,7 @@ func TestParsePlaybookYaml(t *testing.T) { assert.Nil(err1) assert.NotNil(playbookBytes) - playbook, err = parsePlaybookYaml(playbookBytes) + playbook, err = parsePlaybookYaml(playbookBytes, nil) assert.Nil(err) assert.NotNil(playbook) assert.Equal(2, len(playbook.Targets)) @@ -51,3 +51,24 @@ func TestCleanYaml(t *testing.T) { cleanYamlStr = string(cleanYaml(nil)) assert.Equal("\n", cleanYamlStr) } + +func TestTemplateYaml(t *testing.T) { + assert := assert.New(t) + + playbookBytes, err1 := loadLocalFile("../integration/resources/good-postgres-with-template.yml") + assert.Nil(err1) + + var m map[string]string = make(map[string]string) + + m["password"] = "qwerty123" + m["username"] = "animoto" + m["host"] = "theinternetz" + + playbook, err := parsePlaybookYaml(playbookBytes, CLIVariables(m)) + + assert.Nil(err) + + assert.Equal("qwerty123", playbook.Targets[0].Password) + assert.Equal("animoto", playbook.Targets[0].Username) + assert.Equal("theinternetz", playbook.Targets[0].Host) +}