Repository: avenga/couper
Pull request #545 (2022-07-26 10:07:52)

Environment variable
Added `couper.environment` variable

---
<details>
    <summary>Reviewer checklist</summary>
    <ul>
        <li>Read PR description: a summary about the changes is required</li>
        <li>Changelog updated</li>
        <li>Documentation: docs/{Reference, Cli, ...}, Docker and cli help/usage</li>
        <li>Pulled branch, manually tested</li>
        <li>Verified requirements are met</li>
        <li>Reviewed the code</li>
        <li>Reviewed the related tests</li>
    </ul>
</details>



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e597e2bd..33f6f132 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,9 @@
 
 Unreleased changes are available as `avenga/couper:edge` container.
 
+* **Added**
+  * [`environment` block](https://docs.couper.io/configuration/block/environment), [setting](https://docs.couper.io/configuration/block/settings) and [`couper.environment` variable](https://docs.couper.io/configuration/variables#couper) ([#521](https://github.com/avenga/couper/pull/521), ([#534](https://github.com/avenga/couper/pull/534), [#545](https://github.com/avenga/couper/pull/545))
+
 * **Fixed**
   * Disallow empty path parameters ([#526](https://github.com/avenga/couper/pull/526))
   * Basic Auth client authentication with OAuth2 (client ID and secret must be URL encoded) ([#537](https://github.com/avenga/couper/pull/537))
diff --git a/config/configload/helper.go b/config/configload/helper.go
index d687c335..59d461ce 100644
--- a/config/configload/helper.go
+++ b/config/configload/helper.go
@@ -22,7 +22,7 @@ type helper struct {
 }
 
 // newHelper creates a container with some methods to keep things simple here and there.
-func newHelper(body hcl.Body, src [][]byte) (*helper, error) {
+func newHelper(body hcl.Body, src [][]byte, environment string) (*helper, error) {
 	defaultsBlock := &config.DefaultsBlock{}
 	if diags := gohcl.DecodeBody(body, nil, defaultsBlock); diags.HasErrors() {
 		return nil, diags
@@ -31,7 +31,7 @@ func newHelper(body hcl.Body, src [][]byte) (*helper, error) {
 	defSettings := config.DefaultSettings
 
 	couperConfig := &config.Couper{
-		Context:     eval.NewContext(src, defaultsBlock.Defaults),
+		Context:     eval.NewContext(src, defaultsBlock.Defaults, environment),
 		Definitions: &config.Definitions{},
 		Defaults:    defaultsBlock.Defaults,
 		Settings:    &defSettings,
@@ -151,7 +151,7 @@ func (h *helper) resolveBackendDeps() (uniqueItems []string, err error) {
 
 	// do not forget the other ones
 	var standalone []string
-	for def, _ := range h.defsBackends {
+	for def := range h.defsBackends {
 		standalone = append(standalone, def)
 	}
 	items = append(items, standalone)
diff --git a/config/configload/load.go b/config/configload/load.go
index 63447385..5ac09ed6 100644
--- a/config/configload/load.go
+++ b/config/configload/load.go
@@ -69,7 +69,7 @@ func init() {
 	}
 }
 
-func updateContext(body hcl.Body, srcBytes [][]byte) hcl.Diagnostics {
+func updateContext(body hcl.Body, srcBytes [][]byte, environment string) hcl.Diagnostics {
 	defaultsBlock := &config.DefaultsBlock{}
 	if diags := gohcl.DecodeBody(body, nil, defaultsBlock); diags.HasErrors() {
 		return diags
@@ -77,7 +77,7 @@ func updateContext(body hcl.Body, srcBytes [][]byte) hcl.Diagnostics {
 
 	// We need the "envContext" to be able to resolve absolute paths in the config.
 	defaultsConfig = defaultsBlock.Defaults
-	evalContext = eval.NewContext(srcBytes, defaultsConfig)
+	evalContext = eval.NewContext(srcBytes, defaultsConfig, environment)
 	envContext = evalContext.HCLContext()
 
 	return nil
@@ -165,7 +165,7 @@ func LoadFiles(filesList []string, env string) (*config.Couper, error) {
 		Blocks: hclsyntax.Blocks{defaultsBlock},
 	}
 
-	if diags := updateContext(defs, srcBytes); diags.HasErrors() {
+	if diags := updateContext(defs, srcBytes, env); diags.HasErrors() {
 		return nil, diags
 	}
 
@@ -196,7 +196,7 @@ func LoadFiles(filesList []string, env string) (*config.Couper, error) {
 		Blocks: configBlocks,
 	}
 
-	conf, err := LoadConfig(configBody, srcBytes)
+	conf, err := LoadConfig(configBody, srcBytes, env)
 	if err != nil {
 		return nil, err
 	}
@@ -216,17 +216,17 @@ func LoadBytes(src []byte, filename string) (*config.Couper, error) {
 		return nil, diags
 	}
 
-	return LoadConfig(hclBody, [][]byte{src})
+	return LoadConfig(hclBody, [][]byte{src}, "")
 }
 
-func LoadConfig(body hcl.Body, src [][]byte) (*config.Couper, error) {
+func LoadConfig(body hcl.Body, src [][]byte, environment string) (*config.Couper, error) {
 	var err error
 
 	if diags := ValidateConfigSchema(body, &config.Couper{}); diags.HasErrors() {
 		return nil, diags
 	}
 
-	helper, err := newHelper(body, src)
+	helper, err := newHelper(body, src, environment)
 	if err != nil {
 		return nil, err
 	}
diff --git a/docs/website/content/2.configuration/4.block/environment.md b/docs/website/content/2.configuration/4.block/environment.md
index 1a3254e7..9bc49643 100644
--- a/docs/website/content/2.configuration/4.block/environment.md
+++ b/docs/website/content/2.configuration/4.block/environment.md
@@ -1,52 +1,54 @@
-# Environment Block
+# Environment
 
 The `environment` block lets you refine the Couper configuration based on the set
-[environment](/configuration/command-linemd#global-options).
+[environment](../command-line#global-options).
 
 | Block name    | Context  | Label                                            | Nested block(s)                     |
 | :------------ | :------- | :----------------------------------------------- | :---------------------------------- |
 | `environment` | Overall. | &#9888; required, multiple labels are supported. | All configuration blocks of Couper. |
 
 The `environment` block works like a preprocessor. If the label of an `environment`
-block do not match the set [environment](./CLI.md#global-options) value, the preprocessor
-removes this block and their content. Otherwise, the content of the block is applied
+block does not match the set [`COUPER_ENVIRONMENT`](../command-line#global-options) value, the preprocessor
+removes this block and its content. Otherwise, the content of the block is added
 to the configuration.
 
-If the [environment](/configuration/command-linemd#global-options) value set to `prod`, the following configuration
+## Example
+
+Considering the following configuration with the `COUPER_ENVIRONMENT` value set to `prod`
 
 ```hcl
 server {
-    api "protected" {
-        endpoint "/secure" {
-            environment "prod" {
-                access_control = ["jwt"]
-            }
-
-            proxy {
-                environment "prod" {
-                    url = "https://protected-resource.org"
-                }
-                environment "stage" {
-                    url = "https://test-resource.org"
-                }
-            }
+  api "protected" {
+    endpoint "/secure" {
+      environment "prod" {
+        access_control = ["jwt"]
+      }
+
+      proxy {
+        environment "prod" {
+          url = "https://protected-resource.org"
+        }
+        environment "stage" {
+          url = "https://test-resource.org"
         }
+      }
     }
+  }
 }
 ```
 
-produces after the preprocessing the following configuration:
+the result will be:
 
 ```hcl
 server {
-    api "protected" {
-        endpoint "/secure" {
-            access_control = ["jwt"]
+  api "protected" {
+    endpoint "/secure" {
+      access_control = ["jwt"]
 
-            proxy {
-                url = "https://protected-resource.org"
-            }
-        }
+      proxy {
+        url = "https://protected-resource.org"
+      }
     }
+  }
 }
 ```
diff --git a/docs/website/content/2.configuration/4.block/settings.md b/docs/website/content/2.configuration/4.block/settings.md
index 937c61cc..c72b8d2f 100644
--- a/docs/website/content/2.configuration/4.block/settings.md
+++ b/docs/website/content/2.configuration/4.block/settings.md
@@ -7,6 +7,7 @@ gateway instance.
 |:--------------------------------|:---------------|:--------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------|
 | `accept_forwarded_url`          | tuple (string) | `[]`                | Which `X-Forwarded-*` request headers should be accepted to change the [request variables](../modifiers#request) `url`, `origin`, `protocol`, `host`, `port`. Valid values are `"proto"`, `"host"` and `"port"`. The port in `X-Forwarded-Port` takes precedence over a port in `X-Forwarded-Host`. | Affects relative url values for [`sp_acs_url`](saml) attribute and `redirect_uri` attribute within [beta_oauth2](oauth2) & [oidc](oidc). | `["proto","host","port"]`   |
 | `default_port`                  | number         | `8080`              | Port which will be used if not explicitly specified per host within the [`hosts`](server) list.                                                                                                                                                                                  | -                                                                                                                                                                  | -                           |
+| `environment`                   | string         | `""`                | [Environment](../command-line#global-options) Couper is to run in.                                                                                                                                                                                                                      | - | `"prod"` |
 | `health_path`                   | string         | `"/healthz"`        | Health path which is available for all configured server and ports.                                                                                                                                                                                                                     | -                                                                                                                                                                  | -                           |
 | `https_dev_proxy`               | tuple (string) | `[]`                | List of tls port mappings to define the tls listen port and the target one. A self-signed certificate will be generated on the fly based on given hostname.                                                                                                                             | Certificates will be hold in memory and are generated once.                                                                                                        | `["443:8080", "8443:8080"]` |
 | `log_format`                    | string         | `"common"`          | Switch for tab/field based colored view or JSON log lines. Valid values are `"common"` and `"json"`.                                                                                                                                                                                    | -                                                                                                                                                                  | -                           |
@@ -16,10 +17,10 @@ gateway instance.
 | `request_id_accept_from_header` | string         | `""`                | Name of a client request HTTP header field that transports the `request.id` which Couper takes for logging and transport to the backend (if configured).                                                                                                                                | -                                                                                                                                                                  | `X-UID`                     |
 | `request_id_backend_header`     | string         | `Couper-Request-ID` | Name of a HTTP header field which Couper uses to transport the `request.id` to the backend.                                                                                                                                                                                             | -                                                                                                                                                                  | -                           |
 | `request_id_client_header`      | string         | `Couper-Request-ID` | Name of a HTTP header field which Couper uses to transport the `request.id` to the client.                                                                                                                                                                                              | -                                                                                                                                                                  | -                           |
-| `request_id_format`             | string         | `"common"`          | Valid values are `"common"` and `"uuid4"`. If set to `"uuid4"` a rfc4122 uuid is used for `request.id` and related log fields.                                                                                                                                                          | -                                                                                                                                                                  | -                           |
+| `request_id_format`             | string         | `"common"`          | Valid values are `"common"` and `"uuid4"`. If set to `"uuid4"` a RFC 4122 uuid is used for `request.id` and related log fields.                                                                                                                                                          | -                                                                                                                                                                  | -                           |
 | `secure_cookies`                | string         | `""`                | Valid values are `""` and `"strip"`. If set to `"strip"`, the `Secure` flag is removed from all `Set-Cookie` HTTP header fields.                                                                                                                                                        | -                                                                                                                                                                  | -                           |
 | `xfh`                           | bool           | `false`             | Option to use the `X-Forwarded-Host` header as the request host.                                                                                                                                                                                                                        | -                                                                                                                                                                  | -                           |
-| `beta_metrics`                  | bool           | `false`             | Option to enable the Prometheus [metrics](METRICS.md) exporter.                                                                                                                                                                                                                         | -                                                                                                                                                                  | -                           |
+| `beta_metrics`                  | bool           | `false`             | Option to enable the Prometheus [metrics](/observation/metrics) exporter.                                                                                                                                                                                                                         | -                                                                                                                                                                  | -                           |
 | `beta_metrics_port`             | number         | `9090`              | Prometheus exporter listen port.                                                                                                                                                                                                                                                        | -                                                                                                                                                                  | -                           |
 | `beta_service_name`             | string         | `"couper"`          | The service name which applies to the `service_name` metric labels.                                                                                                                                                                                                                     | -                                                                                                                                                                  | -                           |
-| `ca_file`                       | string         | `""`                | Option for adding the given PEM encoded ca-certificate to the existing system certificate pool for all outgoing connections.                                                                                                                                                            | -                                                                                                                                                                  | -                           |
+| `ca_file`                       | string         | `""`                | Option for adding the given PEM encoded CA certificate to the existing system certificate pool for all outgoing connections.                                                                                                                                                            | -                                                                                                                                                                  | -                           |
diff --git a/docs/website/content/2.configuration/5.variables.md b/docs/website/content/2.configuration/5.variables.md
index dad922c2..b167efb0 100644
--- a/docs/website/content/2.configuration/5.variables.md
+++ b/docs/website/content/2.configuration/5.variables.md
@@ -11,9 +11,10 @@ The second evaluation will happen during the request/response handling.
 
 ## `couper`
 
-| Variable                         | Type   | Description                                                                                                                                                                                                                                                                         | Example   |
-| :------------------------------- | :----- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------- |
-| `version`                        | string | Couper's version number                                                                                                                                                                                                                                                             | `"1.3.1"` |
+| Variable                         | Type   | Description                                                                | Example   |
+| :------------------------------- | :----- | :------------------------------------------------------------------------- | :-------- |
+| `version`                        | string | Couper's version number                                                    | `"1.9.2"` |
+| `environment`                    | string | The [environment](../command-line#global-options) Couper currently runs in | `"prod"`  |
 
 ## `env`
 
diff --git a/eval/context.go b/eval/context.go
index bd3e08bb..d95df282 100644
--- a/eval/context.go
+++ b/eval/context.go
@@ -54,7 +54,7 @@ type Context struct {
 	syncedVariables   *SyncedVariables
 }
 
-func NewContext(srcBytes [][]byte, defaults *config.Defaults) *Context {
+func NewContext(srcBytes [][]byte, defaults *config.Defaults, environment string) *Context {
 	var defaultEnvVariables config.DefaultEnvVars
 	if defaults != nil {
 		defaultEnvVariables = defaults.EnvironmentVariables
@@ -62,7 +62,7 @@ func NewContext(srcBytes [][]byte, defaults *config.Defaults) *Context {
 
 	variables := make(map[string]cty.Value)
 	variables[Environment] = newCtyEnvMap(srcBytes, defaultEnvVariables)
-	variables[Couper] = newCtyCouperVariablesMap()
+	variables[Couper] = newCtyCouperVariablesMap(environment)
 
 	return &Context{
 		eval: &hcl.EvalContext{
@@ -73,13 +73,17 @@ func NewContext(srcBytes [][]byte, defaults *config.Defaults) *Context {
 	}
 }
 
+func NewDefaultContext() *Context {
+	return NewContext(nil, nil, "")
+}
+
 // ContextFromRequest extracts the eval.Context implementation value from given request and
 // returns a noop one as fallback.
 func ContextFromRequest(req *http.Request) *Context {
 	if evalCtx, ok := req.Context().Value(request.ContextType).(*Context); ok {
 		return evalCtx
 	}
-	return NewContext(nil, nil)
+	return NewDefaultContext()
 }
 
 func (c *Context) WithContext(ctx context.Context) context.Context {
@@ -603,9 +607,10 @@ func newCtyEnvMap(srcBytes [][]byte, defaultValues map[string]string) cty.Value
 	return cty.MapVal(ctyMap)
 }
 
-func newCtyCouperVariablesMap() cty.Value {
+func newCtyCouperVariablesMap(environment string) cty.Value {
 	ctyMap := map[string]cty.Value{
-		"version": cty.StringVal(utils.VersionName),
+		"environment": cty.StringVal(environment),
+		"version":     cty.StringVal(utils.VersionName),
 	}
 	return cty.MapVal(ctyMap)
 }


Test patch:
diff --git a/config/body/merged_test.go b/config/body/merged_test.go
index 0b6133c7..e2d77d4d 100644
--- a/config/body/merged_test.go
+++ b/config/body/merged_test.go
@@ -149,7 +149,7 @@ block {
 		}
 	}
 
-	hclcontext := eval.NewContext(nil, nil).HCLContext()
+	hclcontext := eval.NewDefaultContext().HCLContext()
 
 	for k, attr := range expectedAttributes {
 		a, exist := resultAttributes[k]
diff --git a/config/runtime/server_internal_test.go b/config/runtime/server_internal_test.go
index 6d3d2d24..2ac27c03 100644
--- a/config/runtime/server_internal_test.go
+++ b/config/runtime/server_internal_test.go
@@ -208,7 +208,7 @@ func TestServer_validatePortHosts(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(subT *testing.T) {
-			tt.args.conf.Context = eval.NewContext(nil, nil)
+			tt.args.conf.Context = eval.NewDefaultContext()
 			tt.args.conf.Settings = &config.DefaultSettings
 
 			logger, _ := logrustest.NewNullLogger()
diff --git a/eval/context_test.go b/eval/context_test.go
index c5c52436..8f5dba9d 100644
--- a/eval/context_test.go
+++ b/eval/context_test.go
@@ -13,6 +13,7 @@ import (
 	"github.com/zclconf/go-cty/cty"
 
 	"github.com/avenga/couper/config/configload"
+	"github.com/avenga/couper/config/parser"
 	"github.com/avenga/couper/config/request"
 	"github.com/avenga/couper/eval"
 	"github.com/avenga/couper/internal/seetie"
@@ -32,7 +33,7 @@ func TestNewHTTPContext(t *testing.T) {
 		}
 	}
 
-	baseCtx := eval.NewContext(nil, nil)
+	baseCtx := eval.NewDefaultContext()
 
 	tests := []struct {
 		name      string
@@ -212,24 +213,40 @@ func TestCouperVariables(t *testing.T) {
 	tests := []struct {
 		name string
 		hcl  string
+		env  string
 		want map[string]string
 	}{
 		{
 			"test",
 			`
-			server "test" {
-				api {}
-			}
+			server "test" {}
+			`,
+			"",
+			map[string]string{"version": utils.VersionName, "environment": ""},
+		},
+		{
+			"environment",
+			`
+			server {}
 			`,
-			map[string]string{"version": utils.VersionName},
+			"bar",
+			map[string]string{"version": utils.VersionName, "environment": "bar"},
 		},
 	}
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(subT *testing.T) {
-			cf, err := configload.LoadBytes([]byte(tt.hcl), "couper.hcl")
+			bytes := []byte(tt.hcl)
+			hclBody, diags := parser.Load(bytes, "couper.hcl")
+			if diags.HasErrors() {
+				subT.Error(diags)
+				return
+			}
+
+			cf, err := configload.LoadConfig(hclBody, [][]byte{bytes}, tt.env)
 			if err != nil {
-				subT.Fatal(err)
+				subT.Error(err)
+				return
 			}
 
 			hclContext := cf.Context.Value(request.ContextType).(*eval.Context).HCLContext()
diff --git a/eval/value_test.go b/eval/value_test.go
index e7b05594..47c11798 100644
--- a/eval/value_test.go
+++ b/eval/value_test.go
@@ -14,7 +14,7 @@ import (
 )
 
 func TestValue(t *testing.T) {
-	evalCtx := NewContext(nil, &config.Defaults{}).HCLContext()
+	evalCtx := NewContext(nil, &config.Defaults{}, "").HCLContext()
 	rootObj := cty.ObjectVal(map[string]cty.Value{
 		"exist": cty.StringVal("here"),
 		"slice": seetie.GoToValue([]string{"1", "2"}),
diff --git a/handler/endpoint_test.go b/handler/endpoint_test.go
index 35fc4212..6745d2ab 100644
--- a/handler/endpoint_test.go
+++ b/handler/endpoint_test.go
@@ -71,7 +71,7 @@ func TestEndpoint_RoundTrip_Eval(t *testing.T) {
 		}},
 	}
 
-	evalCtx := eval.NewContext(nil, nil)
+	evalCtx := eval.NewDefaultContext()
 
 	for _, tt := range tests {
 		t.Run(tt.name, func(subT *testing.T) {
@@ -229,7 +229,7 @@ func TestEndpoint_RoundTripContext_Variables_json_body(t *testing.T) {
 
 				// normally injected by server/http
 				helper.Must(eval.SetGetBody(req, eval.BufferRequest, 1024))
-				*req = *req.WithContext(eval.NewContext(nil, nil).WithClientRequest(req))
+				*req = *req.WithContext(eval.NewDefaultContext().WithClientRequest(req))
 
 				rec := httptest.NewRecorder()
 				rw := writer.NewResponseWriter(rec, "") // crucial for working ep due to res.Write()
@@ -350,7 +350,7 @@ func TestEndpoint_RoundTripContext_Null_Eval(t *testing.T) {
 			} else {
 				req.Header.Set("Content-Type", "application/json")
 			}
-			req = req.WithContext(eval.NewContext(nil, nil).WithClientRequest(req))
+			req = req.WithContext(eval.NewDefaultContext().WithClientRequest(req))
 
 			rec := httptest.NewRecorder()
 			rw := writer.NewResponseWriter(rec, "") // crucial for working ep due to res.Write()
@@ -432,7 +432,7 @@ func TestEndpoint_ServeHTTP_FaultyDefaultResponse(t *testing.T) {
 
 	ctx := context.Background()
 	req := httptest.NewRequest(http.MethodGet, "http://", nil).WithContext(ctx)
-	ctx = eval.NewContext(nil, nil).WithClientRequest(req)
+	ctx = eval.NewDefaultContext().WithClientRequest(req)
 	ctx = context.WithValue(ctx, request.UID, "test123")
 
 	rec := httptest.NewRecorder()
@@ -483,7 +483,7 @@ func TestEndpoint_ServeHTTP_Cancel(t *testing.T) {
 	}, log.WithContext(ctx), nil)
 
 	req := httptest.NewRequest(http.MethodGet, "https://couper.io/", nil)
-	ctx = eval.NewContext(nil, nil).WithClientRequest(req.WithContext(ctx))
+	ctx = eval.NewDefaultContext().WithClientRequest(req.WithContext(ctx))
 
 	start := time.Now()
 	go func() {
diff --git a/handler/producer/request_test.go b/handler/producer/request_test.go
index e76e24d4..b5498a42 100644
--- a/handler/producer/request_test.go
+++ b/handler/producer/request_test.go
@@ -102,7 +102,7 @@ func Test_ProduceExpectedStatus(t *testing.T) {
 		for i, rt := range []producer.Roundtrip{requests, proxies} {
 			t.Run(testNames[i]+"_"+tt.name, func(t *testing.T) {
 
-				ctx := eval.NewContext(nil, nil).WithClientRequest(clientRequest)
+				ctx := eval.NewDefaultContext().WithClientRequest(clientRequest)
 
 				outreq := clientRequest.WithContext(ctx)
 				outreq.Header.Set("X-Status", strconv.Itoa(tt.reflectStatus))
diff --git a/handler/proxy_test.go b/handler/proxy_test.go
index 233ed26a..5927c3fa 100644
--- a/handler/proxy_test.go
+++ b/handler/proxy_test.go
@@ -34,7 +34,7 @@ func TestProxy_BlacklistHeaderRemoval(t *testing.T) {
 	outreq := httptest.NewRequest("GET", "https://1.2.3.4/", nil)
 	outreq.Header.Set("Authorization", "Basic 123")
 	outreq.Header.Set("Cookie", "123")
-	outreq = outreq.WithContext(eval.NewContext(nil, &config.Defaults{}).WithClientRequest(outreq))
+	outreq = outreq.WithContext(eval.NewContext(nil, &config.Defaults{}, "").WithClientRequest(outreq))
 	ctx, cancel := context.WithDeadline(outreq.Context(), time.Now().Add(time.Millisecond*50))
 	outreq = outreq.WithContext(ctx)
 	defer cancel()
diff --git a/handler/transport/backend_test.go b/handler/transport/backend_test.go
index 3bc606ed..95497c44 100644
--- a/handler/transport/backend_test.go
+++ b/handler/transport/backend_test.go
@@ -383,7 +383,7 @@ func TestBackend_director(t *testing.T) {
 			if !ok && tt.expReq.Host != outreq.Host {
 				subT.Errorf("expected same host value, want: %q, got: %q", outreq.Host, tt.expReq.Host)
 			} else if ok {
-				hostVal, _ := hostnameExp.Expr.Value(eval.NewContext(nil, nil).HCLContext())
+				hostVal, _ := hostnameExp.Expr.Value(eval.NewDefaultContext().HCLContext())
 				hostname := seetie.ValueToString(hostVal)
 				if hostname != tt.expReq.Host {
 					subT.Errorf("expected a configured request host: %q, got: %q", hostname, tt.expReq.Host)
diff --git a/internal/test/helper_proxy.go b/internal/test/helper_proxy.go
index 8a2c7efc..18bc00fd 100644
--- a/internal/test/helper_proxy.go
+++ b/internal/test/helper_proxy.go
@@ -41,6 +41,6 @@ func (h *Helper) NewInlineContext(inlineHCL string) hcl.Body {
 	}
 
 	var remain hclBody
-	h.Must(hclsimple.Decode(h.tb.Name()+".hcl", []byte(inlineHCL), eval.NewContext(nil, nil).HCLContext(), &remain))
+	h.Must(hclsimple.Decode(h.tb.Name()+".hcl", []byte(inlineHCL), eval.NewDefaultContext().HCLContext(), &remain))
 	return body.MergeBodies(remain.Inline)
 }
diff --git a/server/testdata/integration/environment/01_couper.hcl b/server/testdata/integration/environment/01_couper.hcl
index 16f5ea2e..101d3178 100644
--- a/server/testdata/integration/environment/01_couper.hcl
+++ b/server/testdata/integration/environment/01_couper.hcl
@@ -3,7 +3,7 @@ environment "test" {
     endpoint "/test" {
       environment "test" "foo" "bar" {
         set_response_headers = {
-          X-Test-Env = "test"
+          X-Test-Env = couper.environment
         }
       }
       proxy {