If else condtion using terrafrom modules - amazon-ec2

I want to create an if else condition in my code. Let's say I want to provision a server, I just want to make sure if (name = abc or name=xyz) & (type=pqr) then my instance type=jkl.
I am unable to set up such a condition in my variables.tf file.
PS: I am a newbie in Terraform(2hrs old).
thanks
here's an example. I want instance_type to automatically pick up a value "pqr"
testabc.tf
module "testabc" {
source ="/modules/xyz"
name = "abc"
hostname = "jdksnkfjsdn"
instance_type = "hfd"
}

The way you have described what you want to achieve is not possible the way you are trying to do it. However, with some additional configuration, that can be done. For example, if you were to create variables for the root module, then you could do what you want.
variable "name" {
type = string
description = "Instance name."
}
variable "type" {
type = string
description = "Some type."
}
Then, in the module call, you would adjust the code to something along the lines of:
module "testabc" {
source = "/modules/xyz"
name = var.name
hostname = "jdksnkfjsdn"
instance_type = (var.name == "abc" || var.name == "xyz" ) && (var.type == "pqr") ? "jkl" : "some other instance type"
}
The above example is using conditional expression [1] in terraform. It is similar to ternary operators in programming languages, as if the first part of the expression evaluates to true, then the value after the ? will be assigned to the instance_type argument. If it evaluates to false, instance_type will be assigned a value after :.
In other words, if
(var.name == "abc" || var.name == "xyz" ) && (var.type == "pqr")
evaluates to true, then instance_type = "jkl". If the above expression is false, then instance_type = "some other instance type".
[1] https://developer.hashicorp.com/terraform/language/expressions/conditionals

I hope that you know what and why you want to achieve this. Adding multiple conditions will make your Terraform script more complicated (read more about Cognitive Complexity), and then you may lose the advantage of using a declarative language like HCL. Yes, you may use it in MANY cases, but in general you may want to have a predictable state that's represented by an explicit Terraform scripts.
Now, you may want to practice more with declarative languages so you may rethink your logic to achieve what you want differently than by using a "conditional default value" which is not possible with Terraform.
From your other question (terraform modules if else condition), I see the following:
if (stack == dev | stack == staging ) & type = xyz):
instance_type =m5.large
else:
instance_type= anything_else
You can make it easier to something like (in pseudo-code):
if (stack != "prod" && type == xyz):
instance_type =m5.large
else:
instance_type= anything_else
And this would make your module:
variable "name" {
type = string
description = "Instance name."
}
variable "environment" {
type = string
description = "Instance environment"
}
variable "type" {
type = string
description = "Some type."
}
module "testabc" {
source = "/modules/xyz"
name = "${var.environment}-${var.name}"
hostname = "jdksnkfjsdn"
instance_type = (var.environment != "prod" && var.type == "pqr") ? "jkl" : "some other instance type"
}
Or, you can have multiple *.tfvars files where you set values for your variables, so you would have configuration like:
# dev.tfvars
name = "my dev instance name"
type = "my-dev-instance-type"
and
# prod.tfvars
name = "my production instance name"
type = "my-prod-instance-type"
Then your module would look like:
variable "name" {
type = string
description = "Instance name."
}
variable "type" {
type = string
description = "Some type."
}
module "testabc" {
source = "/modules/xyz"
name = var.name
hostname = "jdksnkfjsdn"
instance_type = var.type
}
And when applying you pass the -env-file parameter like:
terraform apply -var-file="dev.tfvars"
If none of these approaches is not suitable for you, please refer to #Marko E's answer above since the values in the default argument should be known before, and they shouldn't refer to something else in the configuration.
Also, you may have a look at local values where you can define assign an object or result of an expression to a local variable:
variable "name" {
type = string
description = "Instance name."
}
variable "type" {
type = string
description = "Some type."
}
locals {
isSomething = (var.name == "abc" || var.name == "xyz" ) && (var.type == "pqr")
}
module "testabc" {
source = "/modules/xyz"
name = var.name
hostname = "jdksnkfjsdn"
instance_type = local.isSomething ? "jkl" : "some other instance type"
}

Related

How can I create a complete_name field in a custom module for a custom hierarchy like used on product categories in Odoo?

I'm trying to create a field “complete_name” that displays a hierarchy name similar to whats done on the product categories grid but I can't seem to get it to work. It just puts Odoo in an endless loading screen when I access the relevant view using the new field "complete_name".
I have tried to copy the code used in addons/product/product.py and migrate to work with Odoo 9 API by using compute instead of .function type but it did not work.
Can someone help me understand whats wrong? Below is my model class which works fine without the complete_name field in my view.
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )
complete_name = newFields.Char( compute = '_name_get_fnc', string = 'Name' )
def _name_get_fnc( self ):
res = self.name_get( self )
return dict( res )
Your compute function is supposed to define the value of an attribute of your class, not return a value. Ensure the value you are assigning complete_name is a string.
Also name_get() returns a tuple. I am not sure if you really want a string representation of this tuple or just the actual name value.
Try this
def _name_get_fnc( self ):
self.complete_name = self.name_get()[1]
If you really want what is returned by name_get() then try this.
def _name_get_fnc( self ):
self.complete_name = str(self.name_get())
If you are still having issues I would incorporate some logging to get a better idea of what you are setting the value of complete_name to.
import logging
_logger = logging.getLogger(__name__)
def _name_get_fnc( self ):
_logger.info("COMPUTING COMPLETE NAME")
_logger.info("COMPLETE NAME: " + str(self.name_get()))
self.complete_name = self.name_get()
If this does not make it apparent what the issue is you could always try statically assigning it a value in the off chance that there is a problem with your view.
def _name_get_fnc( self ):
self.complete_name = "TEST COMPLETE NAME"
After further review I think I have the answer to my own question. It turns out as with a lot of things its very simple.
Simply use "_inherit" and inherit the product.category
model. This gives access to all the functions and fields
of product.category including the complete_name field
and computes the name from my custom model data. I was
able to remove my _name_get_func and just use the inherited
function.
The final model definition is below. Once this
update was complete I was able to add a "complete_name" field
to my view and the results were as desired!
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_inherit = 'product.category'
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )

CKQuery with NSPredicate fails when using "CONTAINS" operator

According to Apples Class Reference CKQuery, the operator CONTAINS is one of the supported operators. However, that doesn't seem to work. I have a RecordType called myRecord, and a record with field name name type String. I try to fetch the record with two different predicates, one with "==" operator, and one with CONTAINS operator.
func getRecords() {
let name = "John"
let Predicate1 = NSPredicate(format: "name == %#",name)
let Predicate2 = NSPredicate(format: "name CONTAINS %#",name)
let sort = NSSortDescriptor(key: "Date", ascending: false)
let query = CKQuery(recordType: "myRecord", predicate: Predicate1)
// let query = CKQuery(recordType: "myRecord", predicate: Predicate2)
query.sortDescriptors = [sort]
let operation = CKQueryOperation(query: query)
operation.desiredKeys = ["name", "Date"]
operation.recordFetchedBlock = { (record) in
print(record["name"])
operation.queryCompletionBlock = { [unowned self] (cursor, error) in
dispatch_async(dispatch_get_main_queue()) {
if error == nil {
print ("sucess")
} else {
print("couldn't fetch record error:\(error?.localizedDescription)")
}
}
}
CKContainer.defaultContainer().publicCloudDatabase.addOperation(operation)
}
Using Predicate1, output is:
Optional(John)
sucess
Using Predicate2, output is:
couldn't fetch record error:Optional("Field \'name\' has a value type of STRING and cannot be queried using filter type LIST_CONTAINS")
Also using [c] to ignore casings gives a server issue.
How do I use the operator CONTAINS correctly?
EDIT:
I have now looked closer at the documentation, and seen that CONTAINS can only be used with SELF. Meaning that all String fields will be used for searching. Isn't there a better way?
It's an exception mentioned as below:
With one exception, the CONTAINS operator can be used only to test
list membership. The exception is when you use it to perform full-text
searches in conjunction with the self key path. The self key path
causes the server to look in searchable string-based fields for the
specified token string. For example, a predicate string of #"self
contains 'blue'" searches for the word “blue” in all fields marked for
inclusion in full-text searches. You cannot use the self key path to
search in fields whose type is not a string.
So, you can use 'self' instead of '%K' in order to search sub-text of string field.
For the full document written by Apple

How to assign column name from variable

I want to assign a value to a table column. The column selected needs to be based on a variable. How do you do this?
If #language = "german" than I want to assign #new_word.german = string
#new_word = Word.new
#new_word.german = string
#new_word.save
So how would I assign .german using #language? #new_word.#language :/
x = "german"
#new_word.send("#{x}=", "some value")
#new_word[x] = "some value" # may end up skipping overrides/callbacks, etc though. check the docs.

Non-Static method requires a target?

I have never seen this error before and its very confusing, I am essentially trying to do something where I say find me all locations (will only return one) that match the location name passed in and the type:
string name = columns[40];
Location type = db.Locations.Where(l => l.name == name).FirstOrDefault();
Location loc = db.Locations.Where(l => l.name == name && l.type == type.type).FirstOrDefault();
There's probably a better way to do what I want in one fell swoop, but essentially I get the name from a column (this comes from a csv file), and then say, get me that locations information. After this I say ok now that I have all that jazz, go get me a location with this name and its type.
But I get the error:
Non-Static method requires a target
The top level method all this code runs in is:
static void Main(string[] args){}
Essentially its just a console app. So whats going on?
db is the context class, this should be obvious.
columns is me pulling the data from the csv file, in this case columns[40] would be something like "New York"
Full Error message from the stack trace: {"Non-static method requires a target."}
Note: The question posted as a "possible answer" does not help in this case as the main method I am running this code in is static.
Upon further investigation I found the name and type were null so I did the following fix:
if (name != null)
{
Location type = db.Locations.Where(l => l.name == name).FirstOrDefault();
Location loc = db.Locations.Where(l => l.name == name && l.type == type.type).FirstOrDefault();
locationNearbyId = loc.id;
// More code
}
Alas I still get the error at: Location loc = db.Locations.Where(l => l.name == name && l.type == type.type).FirstOrDefault();
I had this happen to me today. Come to find out, I was doing this:
Player player = db.Players
.Where(p => p.ClubID == course.Club.ID && p.IsActive == true && p.Phone != null)
.ToArray()
.SingleOrDefault(p => p.Phone.FormatPhoneNumber() == phone);
where course.Club was being lazy-loaded via EF from my database. At first, I thought my problem was the FormatPhoneNumber extension, but then found that removing the course.Club.ID fixed the issue:
int clubID = course.Club.ID;
Player player = db.Players
.Where(p => p.ClubID == clubID && p.IsActive == true && p.Phone != null)
.ToArray()
.SingleOrDefault(p => p.Phone.FormatPhoneNumber() == phone);
So, avoid using values gleaned from lazy-loaded objects in subsequent LINQ queries - assign them to local variables and then use those variables in your query.
Turns out that because name and type can be null type has to be set out side the if statement and thus i must check if type and name are null before continueing:
name = collumns[40];
type = db.Locations.Where(l => l.name == name).FirstOrDefault();
if (name != null && type != null)
{
Location loc = db.Locations.Where(l => l.name == name && l.type == type.type).FirstOrDefault();
//More code....
}
It is a fact that the problem is due to the null "type" object.
I would solve this way:
var tType = type?.type;
Location loc = db.Locations.Where(l => l.name == name && (type != null && l.type == tType)).FirstOrDefault();

Replace Linq to Entity value in Projection

I want to relace a value retrieved from a L2E projection to an expanded string.
The table contains a column called Status which can have a value "0" or "1" and in my L2E I have
var trans = from t in db.Donation
select new DonationBO()
{
Status = t.Status
};
What I want is to return either of the strings "Pending" or "Committed" instead of "0" or "1".
How can I do this here?
If Status is a string you could simply do:
var trans = from t in db.Donation
select new DonationBO()
{
Status = t.Status == "0" ? "Pending" : "Committed"
};

Resources