Function to read role, environment file in masterless puppet - ruby

I'm working with Puppet 4.5 in masterless configuration and am trying to create a Puppet function to read a simple config file that assigns roles and environments. I don't have any integration with hiera/facter that I can change.
The file format is:
host1::java_app_node::qa
host2::nodejs_app_node::prod
The Puppet function that will read this file is in a module called homebase. I want to function to return a hash or array of hashes that split the config values. This will let me use them in templates.
In modules/homebase/manifests/init.pp I define:
$role_file = 'puppet://role.lst'
I then created modules/homebase/functions/get_roles.pp as follows:
function homebase::get_roles() {
$func_name = 'homebase::get_roles()'
if ! File.exists?($::homebase::role_file) {
fail("Could not find #{$::homebase::role_file}")
}
hosts = { }
File.open($::homebase::role_file).each |line| {
parts = line.split(/::/)
hosts[parts[0]] = { 'host' => parts[0], 'role' => parts[1], 'env' => parts[2] }
}
return hosts
}
In other classes, I then want to call:
class myapp {
$servers = homebase::get_roles().each | k, v | {
$v['host'] if $v['role'] =~ /myapp/ && $v['env'] == $environment
}
file { 'myapp.cfg':
ensure => file,
path => '/opt/myapp/myapp.cfg',
source => template("/myapp/myapp.cfg.erb"),
mode => '0644',
owner => myuser,
group => myuser,
}
}
Seems like there would be a better way to do this. Am I completely off base?

There turned out to be a much easier way to this rather than try to create a function to read a non-standard configuration file. Instead, I used a site.pp file to create node {} entries. I also parameterized the myapp class to take inputs based on the node.
So my site.pp looks like:
node 'server1.mydomain', 'server2.mydomain' {
$myvar = [ 'val1', 'val2' ]
class { 'myapp':
values => $myvar
}
}
This could probably be improved. One of the issues is with a non-Puppet configuration file I was able to also able to control execution in my bash wrapper script. Much of the need for that went away when, though, with the node definitions.

Related

Fetch credentials depending on environment

I can take credentials like explained in the example taken from here - https://jenkins.io/doc/book/pipeline/syntax/#environment
stage('Example') {
environment {
CREDS = credentials('MY_CREDS_DEV')
}
steps {
sh 'echo hello'
}
}
But what I want to do is to get credentials based on some condition.
For example I have MY_CREDS_DEV and MY_CREDS_QA defined in Jenkins credentials. And I have a property ENV=dev defined in Jenkins 'Prepare an environment for the run' section.
I'd like to access credentials based on my environment, i.e. ENV property.
I tried to use CREDS = credentials('MY_CREDS_' + ${ENV}) and tried to extract strings concatenation to a separate function and call it like CREDS = credentials(concatenate(${ENV})) but I got Internal function call parameters must be strings.
So seems I can put only a string to credentials() function which basically means to hardcode it. But how can I choose which credentials to use - dev or qa?
Use CREDS = credentials('MY_CREDS_' + ENV) or CREDS = credentials("MY_CREDS_${ENV}"). ${ENV} will not become 'dev'but ${'dev'} and therefore is no string.
For completeness:
In fact - after playing aroung with the groovy console - it looks like ${ENV} will try to call a function called $ with the closure parameter {ENV} which in turn would return 'dev'. It would give the same result as ENV if you would have defined a function like:
def $(Closure closure) {
closure()
}
But most probably that's not what you wanted to do.
Got this working in Jenkins:2.190.2 with a little groovy. Haven't tested on earlier versions. Just happens to be the one I'm on now. Works fine with multiple stages.
pipeline {
agent {
label "xxxxx"
}
environment {
ROLE = getRole()
}
stages{
stage("write to s3 etc") {
environment {
AWS = credentials("${ROLE}")
}
steps {
script {
sh"""
aws s3 sync build/ "s3://xxxxxxxxxxxx"
"""
}
}
}
}
}
def getRole() {
def branchName = "${env.BRANCH_NAME}"
if (branchName == "xxxxxx") {
return 'some_credential_string'
}
else {
return 'some_other_credential_string'
}
}
If you would like to use different credentials based on the condition, this could be done with the following example:
stage ("Example") {
steps {
script {
if ( params.TEST_PARAMETER == "test_value1" ) {
withCredentials([string(credentialsId: env.CREDENTIALS_1, variable: 'SOME_VARIABLE')]) {
yourFunction()
}
}
else {
withCredentials([string(credentialsId: env.CREDENTIALS_2, variable: 'SOME_VARIABLE')]) {
yourFunction()
}
}
}
}
}
You would need to define yourFunction in the end of your jenkinsfile. In this case, when TEST_PARAMETER is test_value1 in the job, CREDENTIALS_1 will be used from Jenkins credentials list. When TEST_PARAMETER is different, CREDENTIALS_2 credentials will be used. You could have more options by modifying this to the case loop.
Hope this helps.

How to disable chunkhash in neutrino.js?

I'm looking for a way to disable chunkhash in neutrino.js when building, but didn't find any documentation about it, anyone could help?
Updated:
As in webpack, I can customize the output.filename, in neutrino.js, it seems the string "[name].[hash].bundle.js" is baked in, and there's no way to remove [hash] as far as I can see.
In your .neutrinorc.js file, you can add an additional override function to change the output filename to not include the chunk hash (using neutrino-preset-react as an example:
module.exports = {
use: [
'neutrino-preset-react',
(neutrino) => {
// the original value of filename is "[name].[chunkhash].js"
neutrino.config.output.filename('[name].js');
}
]
};
If you want to change build targets based on an environment variable:
module.exports = {
use: ['neutrino-preset-react'],
env: {
NEUTRINO_TARGET: {
desktop: {
use: [
(neutrino) => neutrino.config.output.filename('[name].js');
]
},
mobile: {
use: [
(neutrino) => neutrino.config.entry('mobile').add('index.mobile.js');
]
}
}
}
};
Then you can run Neutrino twice with differing environments:
NEUTRINO_TARGET=desktop neutrino build
NEUTRINO_TARGET=mobile neutrino build

Handling Two Puppet Classes with the Same Name

I want to use the elasticsearch/elasticsearch module in my own module called rehan. The elasticsearch/elasticsearch module provides a class called elasticsearch. If I also want to create a class in my module that makes use of the one in elasticsearch/elasticsearch, how can I achieve this? I have tried:
class rehan::elasticsearch {
class { 'elasticsearch':
manage_repo => true,
repo_version => '2.2',
require => Class['java']
}
elasticsearch::instance { 'es-01':
require => Package['elasticsearch'],
}
}
The above code errors with:
Error: Duplicate declaration: Class[Rehan::Elasticsearch] is already declared; cannot redeclare at..
In Puppet 3 (even with the future parser!), you need to use:
class { '::elasticsearch':
manage_repo => true,
repo_version => '2.2',
require => Class['java']
}
In Puppet 4, the resolution rules for types, classes and variables changed (it doens't try to resolve them contextually), so your code is valid.

puppet notify Exec doesn't working

Here is my code, don't worry about variable which is already set in original code. I am just putting small snippet here to show you what its doing. Following code updating file /etc/sysctl.d/pgsql.conf but not triggering notify or Exec to reload file. what is wrong here?
$sysctl_config = "/etc/sysctl.d/pgsql.conf"
exec { 'update_sysctl_shmall':
unless => "grep -q ^kernel.shmall ${sysctl_config}",
command => "/bin/echo \"kernel.shmall = ${shmall}\" >> ${sysctl_config}",
}
file { '/etc/sysctl.d/pgsql.conf':
ensure => present,
notify => Exec['reload_sysctl']
}
exec { 'reload_sysctl':
provider => shell,
command => '/bin/sysctl --system',
logoutput => 'on_failure',
refreshonly => true,
}
The following code:
file { '/etc/sysctl.d/pgsql.conf':
ensure => present,
notify => Exec['reload_sysctl']
}
only ensures that /etc/sysctl.d/pgsql.conf file exists. If the file exist it will do nothing, that's why Exec was not triggered to reload the file.
Please check the following links about notifications in puppet 1,2.
UPDATE:
Consider using audit metaparemeter:
file { '/etc/sysctl.d/pgsql.conf':
audit => 'content',
ensure => present,
notify => Exec['reload_sysctl']
}

Keep a Play 2 application private on heroku

I'm using Heroku to host a Play 2 application for the purpose of testing and playing around. I'd like the application to be "private" at this point which means that every aspect of the application should only be visible to certain users.
Normally, I would just use an htaccess file with one single user/password, but that's a specific thing of Apache of course and doesn't help me in this case.
The protection doesn't have to be "strong". The main aim is to keep away bots and random visitors
It would be great if I didn't have to "pollute" the code of my play application. I'd prefer to have some external mechanism to achieve that. If there is no other way than to realize it using play itself, the solution should be loosely coupled from the rest of my play application.
How could I achieve that?
edit: to emphasize it: what I want to achieve won't be part of the final application in production mode. So it neither has to be super secure, nor super engineered.
Adreas example is correct but it is from play 2.1 and in play 2.2 the signature of Filter.apply has changed a little bit, this should work better with 2.2:
class BasicAuth extends Filter {
val username = "stig"
val password = "secretpassword"
override def apply(next: RequestHeader => Future[SimpleResult])(request: RequestHeader): Future[SimpleResult] = {
request.headers.get("Authorization").flatMap { authorization =>
authorization.split(" ").drop(1).headOption.filter { encoded =>
new String(org.apache.commons.codec.binary.Base64.decodeBase64(encoded.getBytes)).split(":").toList match {
case u :: p :: Nil if u == username && password == p => true
case _ => false
}
}.map(_ => next(request))
}.getOrElse {
Future.successful(Results.Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="MyApp Staging""""))
}
}
}
I dont think Heroku offers a solution for this. I ended up implementing a Basic access authentication filter and used it in the Global object. It looks something like this
class HerokuHttpAuth extends Filter {
object Conf {
val isStaging = true // read a config instead of hard coding
val user = "theusername"
val password = "thepassword"
}
override def apply(next: RequestHeader => Result)(request: RequestHeader): Result = {
if (Conf.isStaging) {
request.headers.get("Authorization").flatMap { authorization =>
authorization.split(" ").drop(1).headOption.filter { encoded =>
new String(org.apache.commons.codec.binary.Base64.decodeBase64(encoded.getBytes)).split(":").toList match {
case u :: p :: Nil if u == Conf.user && Conf.password == p => true
case _ => false
}
}.map(_ => next(request))
}.getOrElse {
Results.Unauthorized.withHeaders("WWW-Authenticate" -> """Basic realm="MyApp Staging"""")
}
} else {
next(request)
}
}
}

Resources