Extract filebeat prospector 'fileds' used from all attributes definitions (ruby based) - ruby

I'm currently adding filebeat support for services we provide;
For every service we have several log files I would like to track;
I have 1 common filebeat recipe and I'm distinguishing between the different services' logs in different attributes .rb files;
In those I have a separate definition per log;
All definitions have the same "fileds" configuration;
Can I add it to someplace to be used by all configurations?
My structure:
cookbooks
common
recipes
filebeat.rb
services
attributes
service1.rb
service2.rb
The content of servicesX.rb has the following definitions:
access_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"] },
'fields_under_root' => true
}
audit_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
},
'fields_under_root' => true
}
How can I extract
'fields' => {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
Someplace in the same file (servicesX.rb) so to be used by all log files definitions?
Note: I'm a ruby novice :/
Thank you!!

After feedback and clarification in comments below, OP seems to want to DRY the code and re-use a fields definition.
Simplest is to store it in a variable and then use that:
fields = {
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
}
audit_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => fields
}
This, however, may cause issues with how node is set. It really depends on the flow of the rest of your script. In chef (assuming this is about chef) a node is the context on which the script runs, so setting the fields too early might give issues when that node is used much later:
fields = { hostname: node["opsworks"]["instance"]["hostname"] }
# ... do lots of stuff, like fetching, preparing, connecting and whatnot.
fields # now contains the `node` values as set before connecting etc.
If this is an issue, a better option would be to define a method that returns the fields from a passed-in node:
def fields(node)
{
'hostname' => node["opsworks"]["instance"]["hostname"],
'customer' => node["opsworks"]["instance"]["layers"][0],
'internal_ip' => node["opsworks"]["instance"]["private_ip"],
'ec2id' => node["opsworks"]["instance"]["aws_instance_id"],
'os' => node["opsworks"]["instance"]["os"],
'instance_type' => node["opsworks"]["instance"]["instance_type"]
}
end
Or, cleaned up:
def fields(node)
instance = node["opsworks"]["instance"]
{
hostname: instance["hostname"],
customer: instance["layers"][0],
internal_ip: instance["private_ip"],
ec2id: instance["aws_instance_id"],
os: instance["os"],
instance_type: instance["instance_type"]
}
end
Then use that function:
audit_log = {
'paths' => ['TBD'],
'input_type' => 'log',
'fields' => fields(node)
}

After my research: The benefits of the implementation does not justify the time invested;
Leaving it;

Related

How to skip validation for array that is empty?

I have an incoming post data from js like this
[form] => Array (
[name] => 'a form'
[type] => 'form'
...
[children] => Array (
[0] =>
[1] =>
[2] => Array(
[title] => 'first'
[order] => '1'
...
}
[3] => Array(
[title] => 'second'
[order] => '2'
...
)
...
)
...
)
and rules like
[
'form.name' => 'required|string',
'form.type' => 'required|string',
...
'form.children.*.title' => 'requered|string'
'form.children.*.order' => 'requered|integer'
...
]
What is the best way to completely exclude/skip the form.children arrays that are empty and process the ones with data?
Try this:
[
'form.name' => 'required|string',
'form.type' => 'required|string',
...
'form.children.*.title' => 'sometimes|string'
'form.children.*.order' => 'sometimes|integer'
...
]
Sometimes means, if there is something, follow the next rule(s).
https://laravel.com/docs/8.x/validation#validating-when-present
Addition:
For more complex situations, f.ex. you are not interested in an order value if there is no title, makes sense right? Try this:
[
'form.name' => 'required|string',
'form.type' => 'required|string',
...
'form.children.*.title' => 'sometimes|string'
'form.children.*.order' => 'exclude_if:form.children.*.title,null|integer'
...
]
I have never tested/used this on arrays though.
I managed to completely remove the empty arrays just by filtering them out of the request with laravel's prepareForValidation() method and array_filter(). Worked great for me.
protected function prepareForValidation()
{
$this->merge([
"form" => [
"children" => array_filter($this->form["children"])
]
]);
}
#DimitriMostrey's answer worked as well. His answer is a shorter solution without an additional method. Will accept his answer so anyone facing a similar situation can pick the one that suits the most.

Ruby refactoring, can't seem to get the syntax right

I'm pretty new at Ruby and I've been asked to help out on a program. For some reason for the life of me I can't get my syntax right. If the agent relationship is none?, I want to unshift both the 'series_system_agent_record_creation_by_agent_relationship' and 'series_system_agent_record_ownership_relationship'. I'm using the && in my code but it's not doing what I need it to do so any help would be much appreciated!
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_creation_by_agent_relationship'}
whitelisted['agent_relationships'].unshift({
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
})
end
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_ownership_relationship'}
whitelisted['agent_relationships'].unshift({
'jsonmodel_type' => 'series_system_agent_record_ownership_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.responsible_agency['ref'],
'relator' => 'is_controlled_by',
'start_date' => json.responsible_agency['start_date'],
})
end
whitelisted['responsible_agency'] = json.responsible_agency
whitelisted['creating_agency'] = json.creating_agency
Sorry just a quick edit. To make sense of this the .none? is because I'm using an outdated version of Ruby for an older program. Not something I can change because it isn't my choice. To add I want to refactor this code. I know it already works. I've already tried a few ways and my head can't wrap my head around how to do it. Eg. I tried
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_creation_by_agent_relationship' && 'series_system_agent_record_creation_by_agent_relationship'}
whitelisted['agent_relationships'].unshift({
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
} && {
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
}
)
end
whitelisted['responsible_agency'] = json.responsible_agency
whitelisted['creating_agency'] = json.creating_agency
Hello again! I've now also tried another method as well and I am not running into any errors however something seems to be wrong as this program is whitelisting relationships that can be passed to a react front end. Now none of the relationships are appearing on the react front end using the code below and although I am not running into any errors, it is still not working.
if whitelisted['agent_relationships'].none? {|r| r['jsonmodel_type'] == 'series_system_agent_record_ownership_relationship' && 'series_system_agent_record_creation_by_agent_relationship'}
[{
'jsonmodel_type' => 'series_system_agent_record_ownership_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.responsible_agency['ref'],
'relator' => 'is_controlled_by',
'start_date' => json.responsible_agency['start_date'],
},{
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => json.creating_agency[0]['ref'],
'relator' => 'created_by',
'start_date' => json.creating_agency[0]['start_date'],
},].each { |hsh| ['agent_relationships'].unshift(hsh) }
end
whitelisted['responsible_agency'] = json.responsible_agency
whitelisted['creating_agency'] = json.creating_agency
From what I understand from initial code you want to add series_system_agent_record_creation_by_agent_relationship default only if it's missing in whitelist. Same with series_system_agent_record_ownership_relationship - add only when it's missing.
However your refactoring tries to add both at a time.
Refactoring is usually a process when you refactor little bit and then it gives you idea what else can be refactored.
I would start with replacing long names.
One of the initial versions could be.
def whatever_the_name
# ...
unless has_agent_relationship_of_type?(whitelisted, 'series_system_agent_record_creation_by_agent_relationship')
creating_agency = json.creating_agency[0]
default_agent_relationship = {
'jsonmodel_type' => 'series_system_agent_record_creation_by_agent_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => creating_agency['ref'],
'relator' => 'created_by',
'start_date' => creating_agency['start_date'],
}
whitelisted['agent_relationships'].unshift(default_agent_relationship)
end
unless has_agent_relationship_of_type?(whitelisted, 'series_system_agent_record_ownership_relationship')
responsible_agency = json.responsible_agency
default_agent_relationship = {
'jsonmodel_type' => 'series_system_agent_record_ownership_relationship',
'relationship_target_record_type' => 'agent_corporate_entity',
'ref' => responsible_agency['ref'],
'relator' => 'is_controlled_by',
'start_date' => responsible_agency['start_date'],
}
whitelisted['agent_relationships'].unshift(default_agent_relationship)
end
# ...
end
def has_agent_relationship_of_type?(whitelisted, type)
whitelisted['agent_relationships'].any? {|r| r['jsonmodel_type'] == type }
end
Of course, this shouldn't be considered the final refactored code. It all depends how far you want to get. But hopefully it helps.

Ruby Curl::Multi.http 302 redirect problems

I seem to be having a problem with 302 redirects using curb (Ruby's curl programs)
Here's the code snippet that is ** NOT working** (it's NOT doing the 302 redirect)
easy_options = {:follow_location => true, :enable_cookies => true, :useragent => 'curb', :cookiefile => 'cookie.txt'}
multi_options = {:pipeline => true}
url_fields = [
{
:url => 'https://x.y.z/webapps/login/',
:method => :post,
:follow_location => true,
:enable_cookies => true,
:useragent => 'curb',
:post_fields => {
'user_id' => 'xxxx',
'password' => 'xxxx',
'action' => 'login',
'encoded_pw' => Base64.strict_encode64('xxxx')},
}
]
Curl::Multi.http(url_fields,{:pipeline => true}) do |easy, code, method|
puts easy.header_str
end
Here's the code snippet that appears to be working (it's doing the 302 redirect)
easy_options = {:follow_location => true, :enable_cookies => true, :useragent => 'curb', :cookiefile => 'cookie.txt'}
multi_options = {:pipeline => true}
url_fields = [
{ :url => 'https://x.y.z/webapps/login/',
:post_fields => {
'user_id' => 'xxxx',
'password' => 'yyyy',
'action' => 'login',
'encoded_pw' => Base64.strict_encode64('yyyy')}}
]
Curl::Multi.post(url_fields, easy_options, multi_options) do|easy|
# do something interesting with the easy response
puts easy.last_effective_url
end
Question: Why is the first block not doing the 302 redirect as expected? :follow_location is set to true?
Thanks in advance!
Let me know if you need more information

Adding address to mailchimp using Gibbon gem

Im using the gibbon 0.4.6 with ruby 1.9.3p392, and I tried to add the address of my contacts but I couldn't find the correct format of the parameters.
respuesta = gb.listSubscribe({
:id => lista_id, :email_address => email,
:merge_vars => {'FNAME' => nombre, 'LNAME' => apellido,
'MMERGE3' => ['addr1' => 'aqui', 'addr2' => 'Alla', 'city' => 'Mexico DF',
'zip' => '06700', 'country' => 'MX']
}
})
Update
As Amro suggested, now Im using Gibbon 1.0, but I have the same problem:
I used this
respuesta = gb.lists.subscribe({
:id => lista_id, :email => {:email => email},
:merge_vars => {'FNAME' => nombre, 'LNAME' => apellido,
'MMERGE3' => {'addr1' => 'aqui', 'addr2' => 'Alla', 'city' => 'Mexico DF', 'zip' => '06700', 'country' => 'MX'},
'MMERGE4' => 'Mi nota '
}
})
But the address(MMERGE3) wasn't registered at MailChimp.
Any idea is welcome.
Your current code looks reasonable to me. Have you tried also passing "update_existing" with a value of true? If that address is already subscribed then it won't work otherwise since "update_existing" defaults to false.
Old Answer for API 1.3
I'm Gibbon's maintainer. In this case, MailChimp's docs say the type is an "array," but they mean an associative array (i.e. a Ruby hash). So try something like this:
respuesta = gb.listSubscribe({
:id => lista_id, :email_address => email,
:merge_vars => {'FNAME' => nombre, 'LNAME' => apellido,
'MMERGE3' => {'addr1' => 'aqui', 'addr2' => 'Alla', 'city' => 'Mexico DF',
'zip' => '06700', 'country' => 'MX'}
}
})
Also, API 1.3 has been deprecated. I suggest upgrading to Gibbon 1.0, which hits MailChimp API 2.0. The syntax is a little different so be sure to check out the 2.0 docs and Gibbon's updated README here.

Hash with arrays - get array element

I get hash that contains user role, controller name and list of the controller actions this role can access to.
access = {
'admin' => [ 'users' => ['edit','delete'],
'messages' => ['show','update']
],
'user' => [ 'index' => ['index','sign-out'],
'messages' => ['show','index']
]
}
How can i check what access['admin']['users']['edit'] exists?
access['admin']['users'].include? 'edit'
However, this may be a problem: you're using ... => ['users'=>['edit','delete'],...]
This will create an array with a hash inside. Example:
{'a'=>'b'} #=> {"a"=>"b"}
['a'=>'b'] #=> [{"a"=>"b"}]
So consider using this:
access = {
'admin' => { 'users' => ['edit','delete'],
'messages' => ['show','update']
},
'user' => { 'index' => ['index','sign-out'],
'messages' => ['show','index']
}
}

Resources