github.com/mweagle/Sparta@v1.15.0/docs_source/content/reference/application/environments.md (about)

     1  ---
     2  date: 2016-03-09T19:56:50+01:00
     3  title: Managing Environments
     4  weight: 10
     5  ---
     6  
     7  It's common for a single Sparta application to target multiple *environments*. For example:
     8  
     9  * Development
    10  * Staging
    11  * Production
    12  
    13  Each environment is largely similar, but the application may need slightly different configuration in each context.
    14  
    15  To support this, Sparta uses Go's [conditional compilation](http://dave.cheney.net/2013/10/12/how-to-use-conditional-compilation-with-the-go-build-tool) support to ensure that configuration information is validated at build time.  Conditional compilation is supported via the `--tags/-t` command line argument.
    16  
    17  This example will work through the [SpartaConfig](https://github.com/mweagle/SpartaConfig) sample. The requirement is that each environment declare it's `Name` and also add that value as a Stack [Output](http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html).
    18  
    19  ## Default Configuration
    20  
    21  To start with, create the _default_ configuration. This is the configuration that Sparta uses when provisioning your Stack and defines the environment configuration contract.
    22  
    23  ```go
    24  // +build !staging,!production
    25  // file: environments/default.go
    26  
    27  package environments
    28  
    29  import (
    30    "fmt"
    31    "github.com/Sirupsen/logrus"
    32    "github.com/aws/aws-sdk-go/aws/session"
    33    gocf "github.com/crewjam/go-cloudformation"
    34    sparta "github.com/mweagle/Sparta"
    35  )
    36  
    37  // Name is the default configuration
    38  const Name = ""
    39  ```
    40  
    41  The important part is the set of excluded tags at the top of the file:
    42  
    43  ```go
    44  // +build !staging,!production
    45  ```
    46  
    47  This ensures that the configuration is only eligible for compilation when Sparta goes to `provision` the service.
    48  
    49  ## Environment Configuration
    50  
    51  The next steps are to define the environment-specific configuration files:
    52  
    53  ```go
    54  // +build staging
    55  // file: environments/staging.go
    56  
    57  package environments
    58  
    59  import (
    60    "github.com/Sirupsen/logrus"
    61    "github.com/aws/aws-sdk-go/aws/session"
    62    gocf "github.com/crewjam/go-cloudformation"
    63    sparta "github.com/mweagle/Sparta"
    64  )
    65  
    66  // Name is the production configuration
    67  const Name = "staging"
    68  
    69  ```
    70  
    71  
    72  ```go
    73  // +build production
    74  // file: environments/production.go
    75  
    76  package environments
    77  
    78  import (
    79    "github.com/Sirupsen/logrus"
    80    "github.com/aws/aws-sdk-go/aws/session"
    81    gocf "github.com/crewjam/go-cloudformation"
    82    sparta "github.com/mweagle/Sparta"
    83  )
    84  
    85  // Name is the production configuration
    86  const Name = "production"
    87  
    88  ```
    89  
    90  These three files define the set of compile-time mutually-exclusive sources that represent environment targets.
    91  
    92  ## Segregating Services
    93  
    94  The `serviceName` argument supplied to [sparta.Main](https://godoc.org/github.com/mweagle/Sparta#Main) defines the AWS CloudFormation stack that supports your application.  While the previous files represent different environments, they will collide at `provision` time since they share the same service name.
    95  
    96  The `serviceName` can be specialized by using the `buildTags` in the service name definition as in:
    97  
    98  ```go
    99  fmt.Sprintf("SpartaHelloWorld-%s", sparta.OptionsGlobal.BuildTags),
   100  ```
   101  
   102  Each time you run `provision` with a unique `--tags` value, a new CloudFormation stack will be created.
   103  
   104  *NOTE*: This isn't something suitable for production use as there could be multiple `BuildTags` values.
   105  
   106  ## Enforcing Environments
   107  
   108  The final requirement is to add the environment name as a Stack Output. To annotate the stack with the output value, we'll register a [ServiceDecorator](https://godoc.org/github.com/mweagle/Sparta#ServiceDecoratorHook) and use the same conditional compilation support to compile the environment-specific version.
   109  
   110  The _main.go_ source file registers the workflow hook via:
   111  
   112  ```go
   113  hooks := &sparta.WorkflowHooks{
   114    Context:          map[string]interface{}{},
   115    ServiceDecorator: environments.ServiceDecoratorHook(sparta.OptionsGlobal.BuildTags),
   116  }
   117  ```
   118  
   119  Both _environments/staging.go_ and _environments/production.go_ define the same hook function:
   120  
   121  ```go
   122  func ServiceDecoratorHook(buildTags string) sparta.ServiceDecoratorHook {
   123    return func(context map[string]interface{},
   124      serviceName string,
   125      template *gocf.Template,
   126      S3Bucket string,
   127      S3Key string,
   128      buildID string,
   129      awsSession *session.Session,
   130      noop bool,
   131      logger *logrus.Logger) error {
   132      template.Outputs["Environment"] = &gocf.Output{
   133        Description: "Sparta Config target environment",
   134        Value:       Name,
   135      }
   136      return nil
   137    }
   138  }
   139  ```
   140  
   141  The _environments/default.go_ definition is slightly different. The "default" environment isn't one that our service should actually deploy to. It simply exists to make the initial Sparta build (the one that cross compiles the AWS Lambda binary) compile.  Build tags are applied to the *AWS Lambda* compiled binary that Sparta generates.
   142  
   143  To prevent users from accidentally deploying to the "default" environment, the `BuildTags` are validated in the hook definition:
   144  
   145  ```go
   146  func ServiceDecoratorHook(buildTags string) sparta.ServiceDecoratorHook {
   147    return func(context map[string]interface{},
   148      serviceName string,
   149      template *gocf.Template,
   150      S3Bucket string,
   151      S3Key string,
   152      buildID string,
   153      awsSession *session.Session,
   154      noop bool,
   155      logger *logrus.Logger) error {
   156      if len(buildTags) <= 0 {
   157        return fmt.Errorf("Please provide a --tags value for environment target")
   158      }
   159      return nil
   160    }
   161  }
   162  ```
   163  
   164  ## Provisioning
   165  
   166  Putting everything together, the `SpartaConfig` service can deploy to either environment:
   167  
   168  **staging**
   169  
   170      go run main.go provision --level info --s3Bucket $(S3_BUCKET) --noop --tags staging
   171  
   172  **production**
   173  
   174      go run main.go provision --level info --s3Bucket $(S3_BUCKET) --noop --tags production
   175  
   176  Attempting to deploy to "default" generates an error:
   177  
   178  ```bash
   179  INFO[0000] Welcome to SpartaConfig-                      Go=go1.7.1 Option=provision SpartaVersion=0.9.2 UTC=2016-10-12T04:07:35Z
   180  INFO[0000] Provisioning service                          BuildID=550c9e360426f48201c885c0abeb078dfc000a0a NOOP=true Tags=
   181  INFO[0000] Verifying IAM Lambda execution roles
   182  INFO[0000] IAM roles verified                            Count=1
   183  INFO[0000] Running `go generate`
   184  INFO[0000] Compiling binary                              Name=SpartaConfig_.lambda.amd64
   185  INFO[0008] Executable binary size                        KB=15309 MB=14
   186  INFO[0008] Creating ZIP archive for upload               TempName=/Users/mweagle/Documents/gopath/src/github.com/mweagle/SpartaConfig/SpartaConfig_104207098
   187  INFO[0009] Registering Sparta function                   FunctionName=main.helloWorld
   188  INFO[0009] Lambda function deployment package size       KB=4262 MB=4
   189  INFO[0009] Bypassing bucket expiration policy check due to -n/-noop command line argument  BucketName=weagle
   190  INFO[0009] Bypassing S3 upload due to -n/-noop command line argument  Bucket=weagle Key=SpartaConfig-/SpartaConfig_104207098
   191  INFO[0009] Calling WorkflowHook                          WorkflowHook=github.com/mweagle/SpartaConfig/environments.ServiceDecoratorHook.func1 WorkflowHookContext=map[]
   192  INFO[0009] Invoking rollback functions                   RollbackCount=0
   193  ERRO[0009] Please provide a --tags value for environment target
   194  Error: Please provide a --tags value for environment target
   195  Usage:
   196    main provision [flags]
   197  
   198  Flags:
   199    -i, --buildID string    Optional BuildID to use
   200    -s, --s3Bucket string   S3 Bucket to use for Lambda source
   201    -t, --tags string       Optional build tags to use for compilation
   202  
   203  Global Flags:
   204    -l, --level string   Log level [panic, fatal, error, warn, info, debug] (default "info")
   205    -n, --noop           Dry-run behavior only (do not perform mutations)
   206  
   207  ERRO[0009] Please provide a --tags value for environment target
   208  exit status 1
   209  ```
   210  
   211  ## Notes
   212  
   213    - Call [ParseOptions](https://godoc.org/github.com/mweagle/Sparta#ParseOptions) to initialize  `sparta.OptionsGlobal.BuildTags` field for use in a service name definition.
   214    - An alternative approach is to define a custom [ArchiveHook](https://godoc.org/github.com/mweagle/Sparta#ArchiveHook) and inject custom configuration into the ZIP archive. This data is available at `Path.Join(env.LAMBDA_TASK_ROOT, ZIP_ARCHIVE_PATH)`
   215    - See [discfg](https://github.com/tmaiaroto/discfg), [etcd](https://github.com/coreos/etcd), [Consul](https://www.consul.io/) (among others) for alternative, more dynamic discovery services.