Skip to content

Commit

Permalink
Resolving dependencies in correct environment
Browse files Browse the repository at this point in the history
  • Loading branch information
tesharp committed Sep 17, 2019
1 parent f7ed9d4 commit 2ba6c66
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 11 deletions.
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,9 @@ dependency "logs" {
# Source of dependency, has to be a local file
source = "./logs.hcl"
# Resolve the dependency in separate environment
run_in_separate_env = true
# Override one or all of attributes from dependency backend configuration
backend {
sas_token = "override"
Expand All @@ -285,7 +288,7 @@ One or more dependencies for this deployment. Using dependency block has 2 effec

When resolving the output from a dependency it does this by using the terraform remote_state data source. Using example above it has a dependency on vnet.hcl that provides an output map of all subnets with their ids. Tau will not try to run any of the dependencies as that could require access it does not have, for instance vnet could be deployed in another subscription. Instead it creates a temporary terraform script that defines one `terraform_remote_state` data source for each variable defined in input block. It reads the backend definition from dependency source, but backend configuration can be overriden with the backend block in dependency definition. By doing it this way it should not be necessary to define any `terraform_remote_state` inside the module itself, and reading output from another module only requires access to its state store.

To resolve dependencies correct it will run any hooks and use environment variables defined in dependency when resolving the remote state output. This to ensure that dependency is resolved in correct environment.
By default it will inherit the same environment variables (from hooks as well) as current deployment, unless `run_in_separate_env` attribute is set to true. When this is set to true it will not inherit any environment variables and that dependency will be resolved by running any hooks defined in dependency first. This is useful if dependency is deployed in different subscription.

### data

Expand Down
13 changes: 11 additions & 2 deletions pkg/config/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ var (
// define the backend block in dependency and only define the attributes that should be overridden.
// For instance it can be useful to override token attribute if current module and dependency module
// use different token's for authentication
//
// If RunInSeparateEnv is set to true it should fork a new environment that resolves all
// dependencies in separate process (environment relative to dependency). Otherwise it will
// resolve all dependencies in same environment as current execution.
type Dependency struct {
Name string `hcl:"name,label"`
Source string `hcl:"source,attr"`
Name string `hcl:"name,label"`
Source string `hcl:"source,attr"`
RunInSeparateEnv bool `hcl:"run_in_separate_env,optional"`

Backend *Backend `hcl:"backend,block"`
}
Expand All @@ -38,6 +43,10 @@ func (d *Dependency) Merge(src *Dependency) error {
d.Source = src.Source
}

if src.RunInSeparateEnv {
d.RunInSeparateEnv = src.RunInSeparateEnv
}

if d.Backend == nil && src.Backend != nil {
d.Backend = src.Backend
return nil
Expand Down
22 changes: 16 additions & 6 deletions pkg/terraform/v012/dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ type DependencyProcessor struct {
// acceptApplyFailure should be set if its acceptable that apply fails. Should be set if
// no backend is found or unsupported attribute, most probably means a dependency is not deployed
acceptApplyFailure bool

// runInSeparateEnv will spawn a new environment running all hooks and setting environment variables
// when set to true. Setting to false will make sure it runs in same environment
runInSeparateEnv bool
}

// NewDependencyProcessor creates a new dependencyProcessor structure from input arguments
func NewDependencyProcessor(file *loader.ParsedFile, depFile *loader.ParsedFile, executor *Executor, runner *hooks.Runner) *DependencyProcessor {
func NewDependencyProcessor(file *loader.ParsedFile, depFile *loader.ParsedFile, executor *Executor, runner *hooks.Runner, runInSeparateEnv bool) *DependencyProcessor {
f := hclwrite.NewEmptyFile()

return &DependencyProcessor{
Expand All @@ -44,6 +48,8 @@ func NewDependencyProcessor(file *loader.ParsedFile, depFile *loader.ParsedFile,

executor: executor,
runner: runner,

runInSeparateEnv: runInSeparateEnv,
}
}

Expand All @@ -67,15 +73,19 @@ func (d *DependencyProcessor) Process() (map[string]cty.Value, bool, error) {
debugLog := processors.NewUI(ui.Debug)
errorLog := processors.NewUI(ui.Error)

if err := d.runner.Run(d.DepFile, "prepare", "init"); err != nil {
return nil, false, err
}

options := &shell.Options{
Stdout: shell.Processors(debugLog),
Stderr: shell.Processors(d, errorLog),
WorkingDirectory: dest,
Env: d.DepFile.Env,
Env: d.ParsedFile.Env,
}

if d.runInSeparateEnv {
if err := d.runner.Run(d.DepFile, "prepare", "init"); err != nil {
return nil, false, err
}

options.Env = d.DepFile.Env
}

base := filepath.Base(dest)
Expand Down
4 changes: 2 additions & 2 deletions pkg/terraform/v012/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (g *Generator) generateRemoteBackendBlock(file *loader.ParsedFile, name str
}

func (g *Generator) generateDataProcessor(file *loader.ParsedFile, trav []hcl.Traversal) (*DependencyProcessor, error) {
dataProcessor := NewDependencyProcessor(file, file, g.executor, g.runner)
dataProcessor := NewDependencyProcessor(file, file, g.executor, g.runner, false)

for _, data := range file.Config.Datas {
block, err := g.generateHclWriterBlock("data", []string{data.Type, data.Name}, data.Config.(*hclsyntax.Body))
Expand Down Expand Up @@ -189,7 +189,7 @@ func (g *Generator) generateDepProcessor(file *loader.ParsedFile, dep *config.De
return nil, err
}

depProcessor := NewDependencyProcessor(file, depFile, g.executor, g.runner)
depProcessor := NewDependencyProcessor(file, depFile, g.executor, g.runner, dep.RunInSeparateEnv)
depProcessor.File.Body().AppendBlock(block)

// Find variables using this dependency
Expand Down

0 comments on commit 2ba6c66

Please sign in to comment.