unable to pass dict into tpl function (actually anything but <dot>') - kubernetes-helm

I'm new to helm/go templating, and it seems that I still don't understand how context work. Can someone explain, where is problem and why dict cannot be used like this?
I have this template in file stubs/.test.yaml (which I'd like to use with tpl function):
test: abc
test2: {{.Values.key}}
in values.yaml there is just:
key: value
and I include this template like this:
{{ tpl (.Files.Get "stubs/.test.yaml" ) (.) }}
produces:
test: abc
test2: value
So I loaded file from stubs/.test.yaml, and passed it current (root) context, and .Values.key was found and substituted. As expected.
Now lets redefine stubs/.test.yaml as:
test: abc
test2: {{.key}}
and pass a dict as a context when substituting template, as:
{{ tpl (.Files.Get "stubs/.test.yaml" ) (dict "key" .Values.key) }}
so I'd expect the same result, since I passed dict in place of root context, dict has single value named key, with value taken from .Values.key, which is correctly referenced in that template. But I got:
error calling tpl: cannot retrieve Template.Basepath from values inside tpl function: test: abc
test2: {{.key}}: "BasePath" is not a value
instead. What is happening here?
EDIT: I was testing it more, and it's not about dict. I have to pass . as second parameter into tpl function, otherwise it fails.
Version 3.10

I think I found answer in this issue
IIUC: before tpl starts, it creates object .Template (see builtin_objects) in root context, where there are data about currently processed template. So if you are using tpl you must not change context and pass ., or you must pass something as context, from what is valid $.Template accessible.
So if we're passing dict as in example in my question:
{{ tpl (.Files.Get "stubs/.test.yaml" ) (dict "key" .Values.key) }}
it will not work. But if you extend dict definition like this:
{{ tpl (.Files.Get "stubs/.test.yaml" ) (dict "key" .Values.key "Template" $.Template) }}
it will work now.

Related

Helm template check if boolean value is actually empty not false

How to tell if a value, more specifically a a boolean typed value is empty in gotpl alias helm template?
How to tell if a simple value is empty?
You have multiple options, how to tell if a normal (not boolean) value is empty.
A very brute force and quite long way is to put an if statement around your node such as
{{ if .Values.simpleText }}
isEmpty: false
{{ end }}
This will evaulate isEmpty as false only if there is any value inside .Values.simpleText
A better way to do this is to use the inbuilt empty function:
isEmpty2: {{ empty .Values.simpleText }}
This does the same as the first example just shorter and arguably more
readable
The issue with theese methods is that if you have a node which is a bool theese methods wont work.
#values.yaml
myBool: false
#-----------------------------------------
#template.yaml
isBoolEmpty: {{ empty .Values.myBool }}
#-----------------------------------------
#output
isBoolEmpty: true
This will claim that .Values.myBool is empty even tho it clearly has
a value which is false.
Now there is an option to check if a value is nil
isNil: {{ eq nil .Values.nilValue }}
This will result as true if the value is indeed nil ( nilValue: ) but as soon as you have a value inside nilValue you will get a type exception.
The solution that I found and seemed the easiest to understand and to use is:
{{ if (quote .Values.myBool | empty) }}
isActuallyEmpty: ".Values.myBool is empty"
{{ else }}
isActuallyEmpty: ".Values.myBool has a false or true value"
{{ end }}
I am sorry if this is very trivial but I know that I was struggleing with this question for some time.

Helm using ternary with condition checking if list is empty

I am using ternary operand to set values to a variable in my helm template. I am unable to get the condition to check if a list is defined.
{{- $environment_names:= ternary $service.environments $.Values.default.environment_names $service.environments }}
executing "root-app/templates/applications.yaml" at <$service.environments>: wrong type for value; expected bool; got interface {}
is there a possibility to convert this map to boolean? I tried bool and lengthwhich are not functions in helm.
It seems like you can use the default template function here: if $service.environments is defined, use its value, but if not (if it's zero, nil, empty string, or otherwise "falsey") use the default value.
{{- $environment_names := $service.environments | default $.Values.default.environment_names }}
I figured out how I could make it working
{{- $environment_names:= ternary $service.environments $.Values.default.environment_names (hasKey $service "environments") }}

jinja2: passing a string as a parameter to a macro?

I have the following python code to invoke jinja2. In this code, I want to pass a string as an argument to a macro ...
#!/usr/local/bin/python3
import jinja2
test_template = '''
{% macro testmacro(start, arg1, arg2, arg3) -%}
start_{{arg1}}_{{arg2}}_{{arg3}}
{%- endmacro %}
result = {{ testmacro('begin', A, B, C) }}
'''
template = jinja2.Template(test_template)
rendered = template.render(dict(A='AAA', B='BBB', C='CCC'))
print(rendered)
The result is start_AAA_BBB_CCC. However, I want it to be begin_AAA_BBB_CCC.
And if I call the macro with 'xyz' as its first parameter, then I want the output to be xyz_AAA_BBB_CCC.
I don't want the first parameter to be passed to jinja2 from the caller as another variable. I want it hard-coded inside of the template, itself, as a parameter to the macro call.
How can I do this in jinja2 ?
Thank you very much in advance.
Oh, never mind. The answer is simple:
{% macro testmacro(start, arg1, arg2, arg3) -%}
{{start}}_{{arg1}}_{{arg2}}_{{arg3}}
{%- endmacro %}
I thought I tried that before, and it didn't work, but I must have made some other error that I didn't realize.
As long as the string argument is passed within quotes, this works.

Play framework dynamic template include

I have a template that has to include another template based on the file name that comes from the database. For example, here is a template that takes a String that contains the name of the template file that will be included in another template.
#(sourceCodeFileName: Option[String])
#{sourceCodeFileName match {
case Some(sourceCode) => {
#sourcecode.sourceCodeFileName + "scala.html"
}
}}
Where sourcecode is the package where the actual template resides. For example., if the String parameter to the above template is given as myview, then I want to include myview.scala.html. Is there a way to do this in Play framework?
To inject HTML from a static file to a scala template, you can define a function in your template:
#import scala.io.Source
#injectHtmlFromFile(sourceCodeFilename: String) = { #{
Html(Source.fromFile("static/html/" + sourceCodeFilename + ".html").mkString)
}}
…and call it later in the template this way:
#injectHtmlFromFile(sourceCode.sourceCodeFileName)
Side note
I'm not sure I quite understand the question – I've answered the OP's comment and the same question posted by him on Google Groups.
A Play scala template is a function returning Html. You should obtain the template object in the controller and pass it to the template. But in simple cases it's just easier to pass the rendered Html:
Your template would then look like this:
#(content: Option[Html])
#content
and the controller:
object Application extends Controller {
def index = Action {
val sourceCodeFileName= ...
Ok(Some(Class.forName("views.html."+sourceCodeFileName)
.getConstructor().newInstance().asInstanceOf[() => play.api.templates.Html]()
))
}
}

Pyrocms tag as parameter in another tag

I tried to use {{ page:slug }} as a parameter in my page to get the blog articles from the category of the same name. For example:
Pagename = About me
Slug = about-me
Then create a category with the same name and slugname in Blog with associated articles. Now in pagelayouts I thought I could create the following, but it doesn't seem to work. Does anyone know why not?
{{ blog:posts order-by="created_on" dir="asc" category="{{ page:slug }}" }}
<section class="title">
<h4>
{{ title }}
</h4>
</section>
<section class="item">
<p>{{ intro }}</p>
<p>Read more..</p>
</section>
{{ /blog:posts }}
Solved
I found the answer by asking it face to face to another developer. Since this is a templating language, it doesn't support functionality. It just reads pre-made variables. So I will have to solve this problem by creating another method in pages/plugins.php.
You don't need to try and embed a tag in a string, just pass the tag straight to the attribute.
{{ blog:posts order-by="created_on" dir="asc" category="{{ page:slug }}" }}
Should be:
{{ blog:posts order-by="created_on" dir="asc" category=page:slug }}
Easier than you thought ey?
This is how I solved it using PHP. The below edit checks if the page parameter from the {{ blog:posts }} tag is set. When it is, it grabs the last segment and uses it as category filter in the database query to retreive only those posts:
In system/cms/modules/blog/plugin.php look for the 'posts' function and add a parameter:
$page = $this->attribute('page');
Then use the following statement to check if the parameter has been set and then add a 'where' statement:
if($page) //check if page is set
{
$segment = end($this->uri->segment_array()); //get the last segment from the url
$this->db->where('blog_categories.slug', $segment); //use the segment as filter
}
Now you can create a page containing blog posts from which the categories refer to its pagename like for example: www.website.com/pagename/subpagename/subsubpagename/awesome then use this as pagelayout and it will load a list of blogposts that have 'awesome' as category:
<h3>{{ page:title }}</h3>
{{ blog:posts order-by="created_on" dir="asc" page="true" }}
<h4>{{ title }}</h4>
<p>
{{ intro }}</p>
<p>
Read more..</p>
{{ /blog:posts }}
Instead of using tags i have found a simple solution to avoid tags as much as we can. Here is it.
Instead of using tags call a view in plugin and pass the third parameter as TRUE so that it return string instead of loading view than do any kind of looping and conditional checking in the view as usuall as you do with php o course. No need to meet tags there. After that in plugin where you are calling this view simply return a single variable and use your variable in tags in the page to display content of view.
Here is an example
class Plugin_Home extends Plugin
{
function test()
{
$this->load->model('test/test_m');
$data['test'] = $this->test_m->index();
return $this->load->view('test/test_view',$data , TRUE);
}
}
And in the page you can call it like this
{{ Home:test }}
And get rid of tags for conditioning and looping