As we start to build ever more complex Go-based applications, we start to face challenges when it comes to safely introducing new features, or handling credentials for the likes of databases, or brokers.
Using environment variables is an excellent way to simplify a number of different aspects such as handling credentials across various different environments, as well as simplifying the implementation of feature flags in your system.
In this tutorial, we will be covering:
- How you can effectively get and set environment variables within your Go applications
- How you can subsequently use environment variables as the basis for feature flags within your application.
If you are writing distributed systems in Go, the best practice when it comes to configuration is to store it in the environment.
This practice allows you to really easily deploy a version of your application to say, a development server that has the development database credentials already injected into it’s environment.
Once you are happy with your development efforts, you can then deploy an identical copy of your application to a production server that features the production database credentials already injected into the environment with no additional fuss.
Note - You can read up more about the 12 Factors you should adhere too when developing cloud native applications - III. Config
Reading Environment Variables
With Go, we can read environment variables using the
os package and by calling
os.Getenv() passing in the string name of our environment variable.
Let’s set an environment variable called
DATABASE_PASS on our machine which we can then read in our application and use that to connect to a database should we wish.
Note - I’m using MacOS to write these tutorials, if you are on windows, you may have to use
Next, let’s write out Go program which will be able to read in this
Now, when you run this, you should see the following output:
Awesome, our application has been able to successfully pick up this environment variable and print it out using a simple call to
Setting Environment Variables
Go also has the ability to set environment variables should you so wish. We can do this by again leveraging the
os package and calling
And, if we were to go ahead and run this, we should see that it picks up our environment variable changes:
Note - If you happen to run this again, you should see that
unicornsis again picked up as the original value of
DATABASE_PASS. This is because a child process cannot change it’s parent processes’ environment variables. Any changes you make will only be made for your Go program.
One interesting use of environment variables within Go programs could be as
Feature Flags or
Feature Toggles. These feature flags essentially give us the flexibility in our deployments to switch off new features and revert to older features in the event of an issue.
Note - If you haven’t heard of feature flags before, I highly recommend checking out this article from Martin Fowler here: Feature Flags/Toggles
Let’s take for instance a simple REST API:
We could start this application with our
FEATURE_TOGGLE environment variable set to
TRUE which would enable our “Exciting New Feature”, however, should we notice an issue with this new feature after running it in production, we can kill the process and change the environment variable to
FALSE before quickly restarting our application.
In a distributed system, this downtime should see our application instance being taken out of a load balancer group whilst it’s down and then being re-added to the load balancer group when it is brought back up.
Setup Of Environment Variables
In more complex applications, you may have tens, if not hundreds of different environment variables that need to be set prior to application startup.
Now, there are a number of different techniques when it comes to managing these environment variables. This is something that has a “fuzzy” answer unfortunately, different organizations have different security criteria that mean some perfectly valid solutions aren’t fit for them.
Note - Securing your credentials is something that requires a lot of consideration and time to ensure you get it right. These are only suggestions, but the onus is on you to go and research the solution that is right for your team.
- git-crypt - AGWA/git-crypt - Allows transparent encryption/decryption of files in a git repo, with this you can encrypt a bash file which sets up your config and decrypt it when you need to run it -
- AWS SSM - https://aws.amazon.com/secrets-manager/ - If you are working on AWS specific kit, this allows you to manage credentials through a service -
- Within your CI/CD Tool - Example - https://travis-ci.org/ - Inject your environment credentials once into their service and allow them to inject it into your build jobs automatically with the only manual stage happening
These are just a few potential suggestions, hopefully they give you some indication as to how to handle this in a production environment in a secure fashion.
So, in this tutorial, we have covered both reading and writing environment variables in Go, as well as using these environment variables to implement feature flags, or feature toggles within your application.
Hopefully, you have found this article worthwhile, if you have any comments or suggestions, I’d love to hear them in the comments/suggestions section below!
Note - If you want to keep up to date with new articles, then please feel free to follow me on twitter: @Elliot_f.