コード例 #1
0
ファイル: conf.go プロジェクト: conductant/gohm
// Run the configuration specified in conf against the target.  This will run a series
// of template fetching, applying templates, unmarshaling of the final applied template
// onto the given target object.  After unmarshaling is done, the target's fields are
// examined one by one, by tag, and fields that have template as values are then applied.
func Configure(ctx context.Context, conf Conf, target interface{}, optionalFuncs ...template.FuncMap) error {
	conf.lock.Lock()
	defer conf.lock.Unlock()

	initialData := ContextGetInitialData(ctx)
	if initialData != nil {
		gtemplate.ContextPutTemplateData(ctx, initialData)
	}

	contentType := ContextGetConfigDataType(ctx)

	// Generate a list of functions that will escape the template strings
	// Ex.  "secret" : "{{var "zk://host/path/to/secret"}}"
	// This string will be escaped so that the evaluation happens after the unmarshal step by field tag
	funcs := template.FuncMap{}
	for _, fns := range optionalFuncs {
		funcs = gtemplate.MergeFuncMaps(funcs, fns)
	}

	// Note here we generate escaped versions of function to override what's provided.
	// The actual funcs are used in the struct field-by-field step, for those that are marked by tags.
	stubs := gtemplate.MergeFuncMaps(funcs, generateEscapeFuncsFromFieldTag(target))

	conf.model = map[string]interface{}{}
	for _, url := range conf.Urls {

		// Fetch the config data and execute as if it were template
		applied, err := gtemplate.Execute(ctx, url, stubs)

		if conf.OnDoneExecuteLayer != nil {
			conf.OnDoneExecuteLayer(&conf, url, applied, err)
		}

		if err != nil {
			return err
		}

		// Unmarshal to an intermediate representation
		buff := bytes.NewBuffer(applied)
		err = encoding.Unmarshal(contentType, buff, conf.model)

		if conf.OnDoneUnmarshalLayer != nil {
			conf.OnDoneUnmarshalLayer(&conf, url, err)
		}

		if err != nil {
			return err
		}
	}

	if conf.OnDoneFetching != nil {
		conf.OnDoneFetching(&conf)
	}

	// Now marshal the aggregated model to a buffer and then unmarshal it back to the typed struct
	serialized := new(bytes.Buffer)
	err := encoding.Marshal(contentType, serialized, conf.model)
	if err != nil {
		return err
	}
	conf.serialized = serialized.Bytes()
	err = encoding.Unmarshal(contentType, bytes.NewBuffer(conf.serialized), target)

	if conf.OnDoneSerialize != nil {
		conf.OnDoneSerialize(&conf, err)
	}

	if err != nil {
		return err
	}

	if conf.OnDoneUnmarshal != nil {
		conf.OnDoneUnmarshal(&conf, target)
	}

	// Now look for fields with struct tags and apply the actual templates
	return evalStructFieldTemplates(target, funcs)
}
コード例 #2
0
ファイル: template_test.go プロジェクト: conductant/gohm
func (suite *TestSuiteTemplate) TestTemplateExecuteWithVarBlock(c *C) {
	// Write something
	url := "zk://" + strings.Join(Hosts(), ",")
	zk, err := namespace.Dial(context.Background(), url)
	c.Assert(err, IsNil)
	c.Log(zk)

	k := "/unit-test/registry/template/test-template-execute-vars/PG_PASS"
	p := namespace.NewPath(k)

	err = zk.Delete(p)

	// Write new data
	v := []byte("password")
	_, err = zk.Put(p, v, false)
	c.Assert(err, IsNil)

	zk.Close()

	// Now use the value in zk in the template

	// Generate the auth token required by the server.
	token := auth.NewToken(1*time.Hour).Add("secure", 1)
	header := http.Header{}
	token.SetHeader(header, testutil.PrivateKeyFunc)

	// This is the content to be served by the test server.
	// Using the Execute will add additional functions that can be included in the var blocks.
	suite.template = `
{{define "comments"}}
# The variable define blocks allow the definition of variables that are reused throughout the
# main body of the template.  The variables are referenced as '<blockname>.<fieldname>'.
{{end}}

{{define "app"}} # blockname is 'app'
version: 1.2
image: repo
build: 1234
user: "******"USER"}}"  # Getting the environment variable and use that as value.
password: "******"zk:///unit-test/registry/template/test-template-execute-vars/PG_PASS" }}"
{{end}}

{{define "host"}}
label: appserver
name: myhost
port: {{.port}}  # Here we allow the application to pass in a context that's refereceable.
{{end}}

{
   "image" : "repo/myapp:` + "{{my `app.version`}}-{{my `app.build`}}" + `",
   "host" : "` + "{{my `host.name`}}" + `",{{/* use this for comment in JSON :) */}}
   "user" : "` + "{{my `app.user`}}" + `",
   "password" : "` + "{{my `app.password`}}" + `",
   "port" : "` + "{{my `host.port`}}" + `"
}`

	// The url is a template too.
	url = "http://localhost:{{.port}}/secure"

	data := map[string]interface{}{
		"Name": "test",
		"Age":  20,
		"port": suite.port,
	}
	ctx := template.ContextPutTemplateData(resource.ContextPutHttpHeader(context.Background(), header), data)

	text, err := template.Execute(ctx, url)
	c.Assert(err, IsNil)
	c.Log(string(text))
	obj := make(map[string]string)
	err = json.Unmarshal(text, &obj)
	c.Assert(err, IsNil)
	c.Assert(obj["image"], Equals, "repo/myapp:1.2-1234")
	c.Assert(obj["user"], Equals, os.Getenv("USER"))
	c.Assert(obj["host"], Equals, "myhost")
	c.Assert(obj["port"], Equals, fmt.Sprintf("%d", suite.port))
	c.Assert(obj["password"], Equals, string(v))
}