Go template - syntax for range - go

In Go template, I have a map setup like this:
{{$key}}map := make(map[string]interface{})
And I want to iterate through the map using this:
{{ range $mapKey, $mapValue := {{$key}}map}}
And I am getting this error:
unexpected "{" in range
Looks like it does not allow nested {{}} inside another {{}}. Is there anyway I can solve this issue ???

You cannot generate variable names to be used in templates using the template engine itself. You seem to be in need of having multiple maps, one for each $key. So, use a map of maps:
m := make(map[string]map[string]interface{})
where m[key] gives the map for the key.
Then you can do:
{{ range $mapKey, $mapValue := (index $.m $.key)}}
...
{{end}}

Related

How to load templates in subdirectories

I currently have all my html files in a flat directory templates/ and I load everything in with
tmpl := template.Must(template.ParseGlob("templates/*.html"))
But I'd like to now bring in some structure and put templates into folders, components, base, etc. But when I do my site stops working. I'm thinking it could be the above, or could it also be that I need to reference the path in the template?
example
{{ template "navbar" }}
would become
{{ template "components/navbar" }}
Slightly confused...
I'm also using the native go library not a framework, for now.
Go's glob does not support matching files in sub-directories, i.e. ** is not supported.
You can either use a third party lib (there are a number of implementations on github), or you could invoke filepath.Glob for each "level" of sub-directories and aggregate the returned file names into a single slice and then pass the slice to template.ParseFiles:
dirs := []string{
"templates/*.html",
"templates/*/*.html",
"templates/*/*/*.html",
// ...
}
files := []string{}
for _, dir := range dirs {
ff, err := filepath.Glob(dir)
if err != nil {
panic(err)
}
files = append(files, ff...)
}
t, err := template.ParseFiles(files...)
if err != nil {
panic(err)
}
// ...
You also need to keep in mind how ParseFiles works: (emphasis mine)
ParseFiles creates a new Template and parses the template definitions
from the named files. The returned template's name will have the
(base) name and (parsed) contents of the first file. There must be at
least one file. If an error occurs, parsing stops and the returned
*Template is nil.
When parsing multiple files with the same name in different
directories, the last one mentioned will be the one that results. For
instance, ParseFiles("a/foo", "b/foo") stores "b/foo" as the template
named "foo", while "a/foo" is unavailable.
This means that, if you want to load all the files, you have to ensure at least one of two things: (1) that each file's base name is unique across all template files, not just in the directory in which the file's located, or (2) provide a unique template name for each file by using the {{ define "<template_name>" }} action at the top of the file's contents (and do not forget the {{ end }} to close the define action).
As an example for the 2nd approach, let's say, in your templates you have two files that have the same base name, e.g. templates/foo/header.html and templates/bar/header.html and their contents are as follows:
templates/foo/header.html
<head><title>Foo Site</title></head>
templates/bar/header.html
<head><title>Bar Site</title></head>
Now to give the these files a unique template name you can change the contents to this:
templates/foo/header.html
{{ define "foo/header" }}
<head><title>Foo Site</title></head>
{{ end }}
templates/bar/header.html
{{ define "bar/header" }}
<head><title>Bar Site</title></head>
{{ end }}
After you do this, you can either execute them directly with t.ExecuteTemplate(w, "foo/header", nil), or indirectly by having other templates reference them using the {{ template "bar/header" . }} action.

Substitute a variable in regexp.MatchString method

I have a golang script that needs to create and look for a particular regex. The string to look for id defined as a constant.
const nameRegex = "service-route"
I can use this variable in some places.
rb := &compute.Route{
Name: fmt.Sprintf("%s-%s", nameRegex, generateCode(host))
I would like to use the same string to find aswell.
Basically I have something like
matched, _ := regexp.MatchString("^service-route-.*", route.Name)
if matched {
Doing something like
matched, _ := regexp.MatchString("^%s-.*" , nameRegex, route.Name)
does not work as the function MatchString requires only 1 argument.
I tried something like
myRegex , err := regexp.Compile("%s", nameRegex)
myRegex.MatchString(route.Name)
that too does not work.
Is it even possible to use a variable to match a regex ?
The 1st parameter to MatchString is a string. So use Sprintf (as you did earlier) to generate the pattern string, something like this:
regexp.MatchString(fmt.Sprintf("^%s-.*", nameRegex), route.Name)
or construct the string using concatentation:
regexp.MatchString("^" + nameRegex + "-.*", route.Name)
This seems to be a one-off check, so there is not need to pre-compile the regex.
It is possible. Here is go playground : https://play.golang.org/p/hc9eMcSzGQC

Kubernetes go client: list events

I am trying to get a list of events in the namespace, but with or without FieldSelector I get an empty list. Is this the right way to do it?
eventListOptions := metav1.ListOptions{FieldSelector: fields.OneTermEqualSelector("involvedObject.name", job.Name).String()}
jobEvents, _ := clientset.EventsV1beta1().Events(GetNamespace()).List(eventListOptions)
If you print error return by List, you should get error something like "involvedObject.name" is not a known field selector: only "metadata.name", "metadata.namespace"
use CoreV1 instead of EventsV1beta1
The line will be something like below:
jobEvents, _ := clientset.CoreV1().Events(GetNamespace()).List(eventListOptions)
"involvedObject.name", job.Name isn't supported by EventsV1beta1
Hope it'll help.

Go capturing template variables and names

I am experimenting with Go's templates.
I am curious: is it possible to do the following: {{$myVariable := randomStringFunc | saveFunc}}
Where:
$myVariable - randomly chosen variable name
randomStringFunc - function that generates random strings
TRICKY: saveFunc - a function that saves the variable name and its value
I have looked into capture-or-assign-golang-template-output-to-variable but I am not sure how and if it can help me achieve my goal.
EDIT
In the end I would like to have a mapping between the variable name that is defined in the template and the value that is assigned to it:
var variableMapping map[string]string
After template execution the content of variableMapping should be something like:
{
"$myVariable:"randomString1",
"$anotherVariable":"5",
"$thirdVariableInMyTemplate":"false"
}

Show default content in a template if an object is nil otherwise show based on the set property

In my template, I would like to include some default meta tags (90% of the time). However, when a specific property is set, I would like to show a different set of text.
I know I can set an anonymous struct and set a property with either "default" or "some-x". However, this means, I need to add an anonymous struct to 90% of my handlers that just currently pass nil.
Is there way to do something like
{{if eq . nil}}
// default meta tag
{{else if eq .MetaValue "some-x"}}
//other
{{end}}
If I try something like my above code, it compiles but doesn't do what I want. Appreciate any suggestions on how to handle it properly without adding a lot of boiler plate.
Thanks!
{{if not .}}
output when . is nil or otherwise empty including
false, 0, and any array, slice, map, or string of length zero
{{else if eq .MetaValue "some-x"}}
// some-x case
{{else}}
// other case
{{end}}
If you want to ensure you're only checking against nil and not 0, false, the empty string, or any other falsey type, you can use the kindIs function to accomplish this.
{{ if kindIs "invalid" . }}
// only if variable is literally nil. falsey values will fallthrough.
{{ else if eq .MetaValue "some-x" }}
// other
{{ else }}
// final case, if any
{{ end }}
I've been recently facing an issue with identifying nil vs 0 values in a Helm Chart (which uses Go templates, including sprig) and haven't found any solutions posted, so I thought I'd add mine here.
I came up with a kind of ugly solution which is to quote the value and then check for a string that matches "<nil>" (with quotes, so you'd actually be checking (quote .Values.thing | eq "\"<nil>\"")). This allows differentiating tests against empty values vs defined 0 values. In my case, I was trying to build a config file where some default options were non-0, so when 0 was explicitly set, I wanted to know that 0 was set instead of just omitted.
Hopefully this can be a help to someone else.
It would be nice to have a better way to do this, but so far I haven't found anything that doesn't require creating and adding my own template functions.

Resources