How can I reuse environment specific variables? - tfs

Say we have 2 logical environments "Staging" and "Production".
How or where can I define a variable that will have the value X on staging and Y on production AND be able to reuse those variables when I create more than 1 release definition?
An example would be the URL of a Web API to be used by all applications released on that environment.
For example I have these environment specific variables:
Staging
ApiUrl: https://staging.api.com
Production
ApiUrl: https://production.api.com
And I want to create 2 release definitions to deploy products (that both use that Api) to both environments.
If I add the ApiUrl as environment variables I need to add and maintain those variables on ALL release definition environments. Not very maintainable?
If I create a variable group "StagingVariables" and "ProductionVariables" I don't think I can switch/choose which group to use on which environment? You can link multiple variable groups to a release definition, but not to a specific environment, right?
You can define release definition variables which would combine the worst of the above 2 options.
If there's no decent way to solve this, are there way to help reduce the maintainance burden? Bulk editing environment variables for example?

Your assessment seems to be correct. The only way I can think of is to programmatically overwrite the variable values from a PowerShell script at the start of an environment.
Or, I think you can create a variable in the environment and set the value to a variable in the variable group, eg:
Variable Group Staging
- Staging.ApiUri
Variable Group Prod
- Prod.ApiUri
Environment Staging
- ApiUri = $(Staging.ApiUri)
Environment Prod
- ApiUri = $(Prod.ApiUri)
That way the value is still defined in one location, but explicitly scoped at the release level.

Related

Is there a way to find current environment in a Ruby application similar to rails.env?

I need to implement a feature that sets some Env vars conditionally based on current environment(dev,test,prod). I know in rails this can be accomplished with rails.env. Is there a similar method for Ruby?
No, vanilla ruby doesn't have a concept of "environment". You will have to build this yourself. One simple way is to use an environment variable and read it.
For example, you can require a MYAPP_ENVIRONMENT environment variable. Then you read it with myapp_environment = ENV['MYAPP_ENVIRONMENT']. Then you might have hashes or some other data structure to determine values that are specific to that environment:
ENDPOINT_A = {
prod: 'https://prod.my_company.com',
stage: 'https://stage.my_company.com'
}
Similarly for other variables. Note that the endpoint string is a ruby variable, not an environment variable. You should NOT set environment variables from the ruby code that uses them. The whole point of environment variables is that they are set externally to your app and your app takes them as input to configure how it behaves.
If you want to specify the endpoint through an environment variable, you should set it in the operating system where your ruby app runs. You can do this manually on the machine or through a deployment pipeline or script.

How to test the presence of environnements variables in Rspec

Question
There are many ways to achieve that goal but I would like to know what is the best way to tests the presence of the environmental variables inside a Ruby on Rails project.
Context
We recently had a production issue related to a missing environment variable in one of our Rails project.
To prevent this from happening again, I would like to test the presence of the environments variable in the application.yml configuration file.
I am using Ruby 2.5, Rails 4.2, Spring 2.
Unless checking this specifically in application.yml is an absolute requirement (why?), here is my take. Make a config/initializers/env.rb and put in there something like
%i[FOO BAR BAZ].each do |var|
ENV[var] = ENV.fetch(var)
end
What this does is reads all of your required environment variables (FOO, BAR, BAZ etc.) and Hash#fetch them which will 'raise' if this variable is not set at boot time.

How to avoid passing environment variables to puppet agent

I am trying to use a 3rd party puppet module which relies on the fact the puppet agent which will run has the JAVA_HOME correctly set.
The JAVA_HOME is required because there is a command defined in a provider (see here).
I have 2 options which I can use now:
Pass the variable when executing the puppet agent (but works only in interactive)
Put in place a file to be sourced by the user executing puppet with the cron (self managed by puppet)
My question is: is it possible to pass the environment to a provider?
POSSIBLE SOLUTION
Taking inspiration from the following post
I created a new type and a provider for it which inside sets the ENV[myvar] = myvalue. Note that the type is not ensurable.
The variable will "leak" and will allow other modules relying on it to work properly.
E.g.
### ... Provider code
def ensure
if value = ENV[resource[:name]]
value
else
:absent
end
end
def ensure=(new_value)
if new_value == :absent
ENV.delete(resource[:name])
else
ENV[resource[:name]] = new_value
end
end
### Usage in puppet code
mytype { 'MYVAR':
ensure => 'MYVAL',
}
Note that puppet will report at each run the value has changed from absent to a specific value. To avoid so, I think it is enough to always return :absent (I must verify that).
is it possible to pass the environment to a provider?
No, Puppet has no mechanism for customizing the environment for external commands on a per-provider basis. I think you have more options than those you've enumerated, however. Among them:
wrap the puppet command in a shell script that sets the environment variables you want
if you are indeed using cron to schedule agent runs, then use cron's built-in support for setting environment variables for the commands it runs.
The latter seems a promising alternative.

data driven ios-Calabash automated testing using xml or css or global variables

i am doing automated testing using calabash-ios. I want to be able to run cucumber once and have it run x times for x user names and run through the gamut of test scenarios.
i want to use this:
Given I login as [#{country-name}] user using id [#{Login-name}] and pwd "PASSWORD"
and have a global variable that can store the values for both country and user name.
i had hoped to use scripts to run cucumber x times and set the value for the global variables each time. is this possible? and if so, could someone point me in the right direction?
i tried using :
##Loginname=value
but got this error:
features/step_definitions/common.rb:1: warning: class variable access from toplevel
uninitialized class variable ##Login in Object (NameError)
failing which, will it be possible to access data stored in a xml or css file using calabash?
If you want to run the same cucumber run many times with some different variables you can just use environment variables.
Given I login as "ENV['COUNTRY_NAME']" user using id "ENV['LOGIN_NAME']" and pwd "PASSWORD"
And then when you run the tests
LOGIN_NAME='login name' COUNTRY_NAME=country bundle exec cucumber
And then of course you can put all of the lines you want to run into a bat or sh script.
One thing to be careful of is to use the environment variables or another one to change the path for the outputs so you don't overwrite them.
However, a more elegant solution would be to handle it with a rake task that ran all of the other tasks. The most efficient way to write that would depend on how many different runs you need.
task :all => [:task1, :task2, :task3]
EDIT: To make your scenarios more readable, you should use a generic placeholder in the scenario and hide the environment variables in the step definition.
Given I login as a user
Might have a step definition that looked like:
Given /^I login as a user$/ do
... set up your page object here ...
login_page.login(ENV['COUNTRY_NAME'], ENV['LOGIN_NAME'])
end

Set Rails Variable to Different Values for Different Capistrano Environments

I have a rails app which is set up to deploy with Capistrano to one of two different servers (depending on which the user chooses to deploy to). I want to create an environment variable for the rails app which changes depending on which server the website is deployed to.
In my Capistrano deploy (config/deploy.rb) file, I have a variable "stages" that has the two options for the user to deploy to:
set :stages, %w(production staging)
I have tried to use this variable in an if statement in my environment (config/environment.rb) file to set the variable like so:
if (:stages == "staging")
ENV['GLOBAL_EMAIL'] = "email1#domain.com"
else
ENV['GLOBAL_EMAIL'] = "email2#domain.com"
end
However, when I test this, rails fails to render #{ENV['GLOBAL_EMAIL']} (as far as I can tell because the variable is not set or reachable.
My question is if there is a reason that :stages is not reachable, and also if there is a way/better way to set the variable according to the environment?
Note that even though one is called staging and one is called production, both environments deploy the production environment of rails (not test and production environments of rails), thus I cannot do a test to see which environment rails is running to then set the variable.
For starters you are trying to equate a symbol with a string, which will always return false.
Also within capistrano, to get the environment your deploying in you want the stage variable. This variable is only available after loading and not when the file is read by ruby. What this means is that you need to provide a block that gets lazy loaded. The problem is that these are evaluated in the context of setting a local variable such as deploy_to.
set(:deploy_to) { "/var/www/#{application}/#{stage}" }
This is a common setup within my deploy scripts where staging and production are deployed to the same server.
You could possibly try something like:
set(:global_email) do
if stage == 'production'
ENV['GLOBAL_EMAIL'] = 'email1#example.com'
else
ENV['GLOBAL_EMAIL'] = 'email2#example.com'
end
end
Or another way of handling this is to use the deploy stage specific files.
# config/deploy/production.rb
ENV['GLOBAL_EMAIL'] = 'email1#example.com'
# config/deploy/staging.rb
ENV['GLOBAL_EMAIL'] = 'email2#example.com'
Update
I hadn't noticed the question mentioned rendering the value in Rails. This of course has nothing to do with Rails, nor is anything set in a Capistrano config file available when Rails is initialized since capistrano should really be only available in development environments.
A proper way to do this would be using a rails initalizer and setting the variable when Rails boots up.
# config/initializer/global_email.rb
if Rails.env.production?
GLOBAL_EMAIL = 'email1#example.com'
else
GLOBAL_EMAIL = 'email2#example.com'
end
The final solution I ended up with was to create a file external from my Capistrano deploy (for instance in /home/[user]/.var/.emails) on each server where each file contains just the desired email (e.g. email1#domain.com). Then in environments.rb:
ENV['GLOBAL_EMAIL'] = begin IO.read("/home/[user]/.vars/.email") rescue "" end

Resources