Terraform conditional source in MODULE - terraform

I am trying to set a module's source (this IS NOT a resource) based on a conditional trigger but it looks like the module is getting fired before the logic is applied:
module "my_module" {
source = "${var.my_field == "" ? var.standard_repo : var.custom_repo}"
stuff...
more stuff...
}
I have created the standard_repo and custom_repo vars as well and defined with URLs for respective repos (using git:: -- this all works w/o conditional)
All this being said, anyone know of a way to implement this conditional aspect? (again, this is a module and not a resource)
I tried using duplicate modules and calling based off the var value but this, too, does not work (condition is never met, even when it is):
repo = ["${var.my_field == "na" ? module.my_module_old : module.my_module_new}"]

One way to achieve this is described in this post
Basically, a common pattern is to have several folders for different environments such as dev/tst/prd. These environments often reuse large parts of the codebase. Some may be abstracted as modules, but there is still often a large common file which is either copy-pasted or symlinked.
The post offers a way that doesn't conditionally disable based on variables but it probably solves your issue of enabling a module based on different enviornments. It makes use of the override feature of terraform and adds a infra_override.tf file. Here, it defines a different source for the module which points to an empty directory. Voila, a disabled module.

Variables are not allowed to be used in the module source parameter. There also does not seem to be a plan for this to change. https://github.com/hashicorp/terraform/issues/1439 . Creating a wrapper script , or using something like mustache http://mustache.github.io/ seems to be the best way to solve the problem.

Related

How to reference hiera variable elsewhere in hiera?

We are using the roles pattern in puppet with hiera, meaning we have these lines in hiera.yaml:
- name: "Roles data"
path: "roles/%{::server_role}.yaml"
We have a custom fact that produces the role name when facter runs, but we would like to move this into hiera. Instead of the server_role variable being produced by facter, we want to specify the server_role inside of hiera, and let that variable be referenced elsewhere in hiera. Something like this:
hiera.yaml:
- name: "Per-node data"
path: "nodes/%{trusted.certname}.yaml"
- name: "Roles data"
path: "roles/%{lookup(server_role)}.yaml"
nodes/hostname.yaml:
server_role: foo_bar
I have seen this question, which says to use hiera() or lookup() but when I try to use those, I get this error message:
Interpolation using method syntax is not allowed in this context
So how can I use a hiera variable that's defined elsewhere in hiera?
Edit:
The prototypical code examples for defining roles could use any fact that's known to facter, often giving examples that are based on hostname. When you can't embed server config into hostname, a common(ish) workaround is to write a file such as /etc/server_role, but it seems to defeat the purpose of config management, when you need to ssh into a machine and edit a file. As the other comments & answer here so far mentioned, you could use an ENC, but again, the goal here is not to have config stored outside of version control. In fact, we have foreman as an ENC and we make a practice to never use it that way because then upgrades and other maintenance become unsustainable.
We could write a class which will pick up data from hiera, write it to /etc/server_role, and on the next puppet run, facter will pick that up and send it back to hiera, so then we'll have the server_role fact available to use in hiera.yaml. As gross as this sounds, so far, it's the best known solution. Still looking for better answers to this question.
Thanks.
As #MattSchuchard explained in comments, you cannot interpolate Hiera data into your Hiera config, because the config has to be known before the data can be looked up.
If you need a per-role level in your data hierarchy then an alternative would be to assign roles to machines via an external node classifier. You don't need it to assign any classes, just the server_role top-scope variable and probably also environment.
On the other hand, maybe you don't need a per-role level of your general hierarchy in the first place. Lots of people do roles & profiles without per-role data, but even if you don't want to do altogether without then it may be that module-specific data inside the module providing your role classes could be made to suffice.

How to invoke whatever generator rule is configured for a concrete instance of an abstract concept?

I have a collection of nodes of concept Command that I'm iterating over with a $LOOP$ macro. Command is an abstract concept. I have defined templates and reduction rules for concrete subconcepts, such as Outline:
template tpl_Outline
input Outline
...
and
reduction rules:
[concept Outline ] --> tpl_Outline
[inheritors false ]
[condition <always>]
Question: How would I invoke the appropriate generator rule for the concrete concept from inside the $LOOP$ macro where the nodes are only known to be of the abstract type Command?
[EDIT] Since the proposed answer is specific to looping over a collection of elements, how would I do the same when there's no looping? That is, how to trigger the configured rule for a given node (e.g. a certain child of the current node).
Note 1: I tried using just $LOOP$[null], hoping for the element nodes to be processed by appropriate rules automatically, but that just produced nulls in the output.
Note 2: I tried $LOOP[$COPY_SRC$[null]], but that produced
textgen error: 'No textgen for Draw.structure.Outline' in [actualArgument] Outline null[847086916111387210] in Draw.sandbox#0
[EDIT 2] This is actually a working solution. What helped was probably invalidating the caches (just Rebuild Project was not working).
Note 3: Previously I used a template switch to call an appropriate template based on concrete concept, but I now want to support custom extensions of Command so I can no longer create an exhaustive template switch.
Try using $COPY_SRCL$ (L stands for Loop here), this macro is designed exactly for your situation.
Also, template switches are extensible
Regarding your Build --> Rebuild Project problem: sometimes File --> Invalidate caches can help to resolve such problems.

Puppet iteration from external file

I'm new to configuration management, just FYI.
I'm trying to puppetize elasticsearch, and want to have a master list of elasticsearch nodes in a file (which can be used for multiple things, not just this purpose).
I would like to add elasticsearch.yml via an ERB template and expand the list of FDQN's into the discovery.zen.ping.unicast.hosts: [] param.
For example I have an external file called es_hosts in module/files that contains:
host1.domain.com
host2.domain.com
host3.domain.com
host4.domain.com
Then when puppet builds the ERB template have this in the param:
discovery.zen.ping.unicast.hosts: ["host1.domain.com", "host2.domain.com", "host3.domain.com", "host4.domain.com"]
I've tried a few things, but I can't get my head wrapped around it.
I would be using this list for other things like building firewall rules, etc, so I'd like to have one master list for reference that can be updated by my team.
Thanks for any help!
Rather than have a list in a file, it would be better to have it in Hiera, since defining lists and other external data is specifically what Hiera is for.
(If you have not used Hiera yet, you definitely should read up on it.)
So in Hiera you would have:
---
es_hosts:
- host1.domain.com
- host2.domain.com
- host3.domain.com
- host4.domain.com
In your manifest, you would read that in from Hiera using the hiera function:
$es_hosts = hiera('es_hosts')
(Note that instead of the hiera function, we often use Puppet's Automatic Parameter Lookup feature instead to read data into our manifests from Hiera, but your requirement here - a list of ES hosts to be used in multiple contexts - suggests you will want this list not to be bound to a specific class input. If this does not make sense to you right now, you will need to learn about Parameterised Classes and Automatic Parameter Lookup, but it's otherwise not relevant to this answer.)
Finally, in your ERB template you would have:
discovery.zen.ping.unicast.hosts: ["<%= #es_hosts.join('", "') %>"]
Pay attention to the fact that the $es_hosts variable from your manifest is accessed via a Ruby instance variable #es_hosts in your ERB template.
Finally, note that there is an Elasticsearch Puppet module available on the Puppet Forget here. You may find that using that module is better than writing your own.

Cucumber feature outlines

Is it possible to parameterise a feature file in the same way it is a scenario? So each scenario in the feature could refer to some variables which are later defined by a single table for the entire feature file?
All of the answers I've found so far (Feature and scenario outline name in cucumber before hook for example) use Ruby meta-programming, which doesn't inspire much hope for the jvm setup I'm using.
No its not, and for good reason. Feature files are meant to be simple and readable, they are not for programming. Even using scenario outlines and tables is generally not a good thing, so taking this further and having a feature that cannot be understood without reading some other thing that defines variables is counter productive.
You can however put all your variables and stuff in step definitions and write your feature at a higher level of abstraction. You'll find implementing this much easier, as you can use a programming language (which is good at this stuff).
One way of parameterising a feature file is to generate it from a template at compile-time. Then at runtime your cucumber runner executes the generated feature file.
This is fairly easy to do if you are using gradle. Here is an example:
In build.gradle, add groovy code like this:
import groovy.text.GStringTemplateEngine
task generateFeatureFiles {
doFirst {
File featuresDir = new File(sourceSets.main.output.resourcesDir, "features")
File templateFile = new File(featuresDir, "myFeature.template")
def(String bestDay, String currentDay) = ["Friday", "Sunday"]
File featureFile = new File(featuresDir, "${bestDay}-${currentDay}.feature")
Map bindings = [bestDay: bestDay, currentDay: currentDay]
String featureText = new GStringTemplateEngine().createTemplate(templateFile).make(bindings)
featureFile.text = featureText
}
}
processResources.finalizedBy(generateFeatureFiles)
myFeature.template is in the src/main/resources/features directory and might look like this:
Feature: Is it $bestDay yet?
Everybody wants to know when it's $bestDay
Scenario: $currentDay isn't $bestDay
Given today is $currentDay
When I ask whether it's $bestDay yet
Then I should be told "Nope"
Running the build task will create a Friday-Sunday.feature file in build/src/main/resources with the bestDay and currentDay parameters filled in.
The generateFeatureFiles custom task runs immediately after the processResources task. The generated feature file can then be executed by the cucumber runner.
You could generate any number of feature files from the feature template file. The code could read in parameters from a config file in your resources directory for example.

Creating a spec helper in rubymotion

I have some common methods used in a couple different specs, I want to extract them to some place like a spec helper that is accessible from all specs. Anyone know how to do this?
Here is something that sorta quacks like a spec_helper.
# _spec_helper.rb
module SpecHelper
::App::Persistence = {}
# global `before :each` ish
def self.extended(base)
base.before do
::App::Persistence.clear
end
end
def foo_helper
end
end
And then use it:
# my_view_spec.rb
describe "MyView" do
extend SpecHelper
before do
foo_helper
end
...
Two things to bear in mind:
Spec helper file is named in such way that it gets loaded first (leading underscore)
When running individual specs (e.g. files=my_view_spec.rb) helper file must go along - files=spec/my_view_spec.rb,spec/_spec_helper.rb
I just throw my common methods used in specs as they are (not encapsulated in a Module or anything) in a spec/support/utilities.rb file and Rubymotion seems to pick them up fine, though I don't know if this is the "proper" way to do this.
According to current http://www.rubymotion.com/developer-center/articles/testing/#_spec_helpers
Spec helpers are created under the spec/helpers directory of a RubyMotion project. An example could be spec/helpers/extension.rb.

Resources