Repository: klothoplatform/klotho (go)

Issue:
Kubernetes/Helm plugin does not import bucket name as environment variable
with our change to make s3 bucket names and go secrets environment variables, we need to make the consistent change in the kubernetes plugin

```
(base) jordansinger@Jordans-MacBook-Pro test_pulumi_destroy % kubectl logs executor-844bc54bd8-f74qd -p
Listening on :3000
2023/03/13 22:36:47 s3blob.OpenBucket: bucketName is required
```

We should also warn if an env var is in the exec unit that the plugin doesnt recognize so that we can catch this

Pull request:
add all env vars to helm chart
• Does any part of it require special attention?
• Does it relate to or fix any issue? closes #353 

We will now read all the units environment variables and add them to the helm chart with values substitution. This is how we currently tie in all persist objects today, so this handles all downstream dependencies. Once we have deps which dont use this mechanism we will need to add logic to support those

ran a fresh deploy against the test suite in go
```
======================================================================================================================================== test session starts ========================================================================================================================================
platform darwin -- Python 3.10.10, pytest-7.2.0, pluggy-1.0.0
rootdir: /Users/jordansinger/workspace/infra-tests/integ-tests, configfile: pytest.ini
plugins: anyio-3.6.2
collected 38 items / 27 deselected / 11 selected

tests/common/exec/test_embed_assets.py x.                                                                                                                                                                                                                                                     [ 18%]
tests/common/persist/test_fs.py .x...                                                                                                                                                                                                                                                         [ 63%]
tests/common/persist/test_orm_envvar.py ..                                                                                                                                                                                                                                                    [ 81%]
tests/common/persist/test_secret.py ..                                                                                                                                                                                                                                                        [100%]

=========================================================================================================================== 9 passed, 27 deselected, 2 xfailed in 14.17s =
```

### Standard checks

- **Unit tests**: Any special considerations? added unit test
- **Docs**: Do we need to update any docs, internal or public?
- **Backwards compatibility**: Will this break existing apps? If so, what would be the extra work required to keep them working?


Patch:
diff --git a/.github/workflows/govulncheck.yaml b/.github/workflows/govulncheck.yaml
index 9892ae7f..c985e076 100644
--- a/.github/workflows/govulncheck.yaml
+++ b/.github/workflows/govulncheck.yaml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - uses: actions/setup-go@v3
         with:
-          go-version: '1.19.6'
+          go-version: '1.20.2'
       - uses: actions/cache@v2
         with:
           path: |
diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index 673d3a10..b587004c 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - uses: actions/setup-go@v3
         with:
-          go-version: '1.19.6'
+          go-version: '1.20.2'
       - uses: actions/checkout@v3
       - uses: actions/cache@v2
         with:
diff --git a/.github/workflows/prettier.yaml b/.github/workflows/prettier.yaml
index db3523df..44b8aaa5 100644
--- a/.github/workflows/prettier.yaml
+++ b/.github/workflows/prettier.yaml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - uses: actions/setup-go@v3
         with:
-          go-version: '1.19.6'
+          go-version: '1.20.2'
       - uses: actions/checkout@v3
       - name: List files to check
         run: |
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index e3428980..c0b82331 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -25,7 +25,7 @@ jobs:
       - name: Setup Go
         uses: actions/setup-go@v3
         with:
-          go-version: '1.19.6'
+          go-version: '1.20.2'
 
       - name: Setup Zig
         uses: goto-bus-stop/setup-zig@v1
diff --git a/.github/workflows/staticcheck.yaml b/.github/workflows/staticcheck.yaml
index 6bad3713..7a323f84 100644
--- a/.github/workflows/staticcheck.yaml
+++ b/.github/workflows/staticcheck.yaml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - uses: actions/setup-go@v3
         with:
-          go-version: '1.19.6'
+          go-version: '1.20.2'
       - uses: actions/checkout@v3
       - uses: actions/cache@v2
         with:
diff --git a/pkg/analytics/tracking_utils.go b/pkg/analytics/tracking_utils.go
index 712855ff..20d9d777 100644
--- a/pkg/analytics/tracking_utils.go
+++ b/pkg/analytics/tracking_utils.go
@@ -5,11 +5,11 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
-	"go.uber.org/zap"
-	"math"
 	"net/http"
 	"time"
 
+	"go.uber.org/zap"
+
 	"github.com/klothoplatform/klotho/pkg/core"
 )
 
@@ -53,11 +53,6 @@ func CompressFiles(input *core.InputFiles) ([]byte, error) {
 			UncompressedSize64: uint64(buf.Len()),
 			Modified:           now,
 		}
-		if header.UncompressedSize64 >= math.MaxUint32 {
-			header.UncompressedSize = math.MaxUint32
-		} else {
-			header.UncompressedSize = uint32(header.UncompressedSize64)
-		}
 
 		headerWriter, err := zipWriter.CreateHeader(header)
 		if err != nil {
diff --git a/pkg/core/topology.go b/pkg/core/topology.go
index e9a3bc11..e8e7287c 100644
--- a/pkg/core/topology.go
+++ b/pkg/core/topology.go
@@ -61,11 +61,13 @@ var DiagramEntityToImgPath = TopoMap{
 	{Kind: string(PersistRedisClusterKind)}: "generic/storage/storage.png",
 	{Kind: PubSubKind}:                      "generic/blank/blank.png",
 	{Kind: ConfigKind}:                      "generic/storage/storage.png",
+	{Kind: StaticUnitKind}:                  "generic/storage/storage.png",
 
 	// Use AWS as the ultimate fallback for the Kind, so don't specify Provider.
 	{Kind: GatewayKind, Provider: ProviderAWS, Type: "apigateway"}:      "aws/network/api-gateway.png",
 	{Kind: GatewayKind, Provider: ProviderAWS, Type: "alb"}:             "aws/network/elb-application-load-balancer.png",
 	{Kind: ExecutionUnitKind, Provider: ProviderAWS}:                    "aws/compute/lambda.png",
+	{Kind: StaticUnitKind, Provider: ProviderAWS}:                       "aws/compute/simple-storage-service-s3.png",
 	{Kind: string(PersistKVKind), Provider: ProviderAWS}:                "aws/database/dynamodb.png",
 	{Kind: string(PersistFileKind), Provider: ProviderAWS}:              "aws/compute/simple-storage-service-s3.png",
 	{Kind: string(PersistSecretKind), Provider: ProviderAWS}:            "aws/security/secrets-manager.png",
@@ -104,6 +106,7 @@ var DiagramEntityToCode = TopoMap{
 
 	{Kind: GatewayKind}:                     `generic_network.Subnet("%s")`,
 	{Kind: ExecutionUnitKind}:               `generic_compute.Rack("%s")`,
+	{Kind: StaticUnitKind}:                  `generic_storage.Storage("%s")`,
 	{Kind: string(PersistKVKind)}:           `generic_storage.Storage("%s")`,
 	{Kind: string(PersistFileKind)}:         `generic_storage.Storage("%s")`,
 	{Kind: string(PersistSecretKind)}:       `generic_storage.Storage("%s")`,
@@ -117,6 +120,7 @@ var DiagramEntityToCode = TopoMap{
 	{Kind: GatewayKind, Provider: ProviderAWS, Type: "alb"}:        `aws_network.ElbNetworkLoadBalancer("%s")`,
 
 	{Kind: ExecutionUnitKind, Provider: ProviderAWS}:                    `aws_compute.Lambda("%s")`,
+	{Kind: StaticUnitKind, Provider: ProviderAWS}:                       `aws_storage.S3("%s")`,
 	{Kind: string(PersistKVKind), Provider: ProviderAWS}:                `aws_database.Dynamodb("%s")`,
 	{Kind: string(PersistFileKind), Provider: ProviderAWS}:              `aws_storage.S3("%s")`,
 	{Kind: string(PersistSecretKind), Provider: ProviderAWS}:            `aws_security.SecretsManager("%s")`,
diff --git a/pkg/exec_unit/plugin_assets.go b/pkg/exec_unit/plugin_assets.go
index 7700ae15..eca6dd0a 100644
--- a/pkg/exec_unit/plugin_assets.go
+++ b/pkg/exec_unit/plugin_assets.go
@@ -38,6 +38,7 @@ func (p Assets) Transform(result *core.CompilationResult, deps *core.Dependencie
 			if annot.Capability.Name != annotation.AssetCapability {
 				continue
 			}
+			log := zap.L().With(logging.FileField(f), logging.AnnotationField(annot)).Sugar()
 
 			includes, _ := annot.Capability.Directives.StringArray("include")
 			excludes, _ := annot.Capability.Directives.StringArray("exclude")
@@ -56,19 +57,20 @@ func (p Assets) Transform(result *core.CompilationResult, deps *core.Dependencie
 			matchCount := 0
 			for _, asset := range input.Files() {
 				if matcher.Matches(asset.Path()) {
-					zap.L().With(logging.FileField(f), logging.AnnotationField(annot)).Sugar().Infof("Adding asset '%s' to unit '%s'", asset.Path(), destUnit)
 					matchCount++
 					if destUnit == "" {
+						log.Infof("Adding asset '%s' to all units", asset.Path())
 						for _, unit := range units {
 							unit.AddStaticAsset(asset)
 						}
 					} else {
+						log.Infof("Adding asset '%s' to unit '%s'", asset.Path(), destUnit)
 						units[destUnit].AddStaticAsset(asset)
 					}
 				}
 			}
 			if matchCount == 0 {
-				zap.L().With(logging.FileField(f), logging.AnnotationField(annot)).Warn("No assets found matching include/exclude rules")
+				log.Warn("No assets found matching include/exclude rules")
 			}
 			if matcher.err != nil {
 				errs.Append(matcher.err)
diff --git a/pkg/infra/kubernetes/exec_unit.go b/pkg/infra/kubernetes/exec_unit.go
index db492881..3eb74404 100644
--- a/pkg/infra/kubernetes/exec_unit.go
+++ b/pkg/infra/kubernetes/exec_unit.go
@@ -326,6 +326,24 @@ func (unit *HelmExecUnit) getServiceName() string {
 	return unit.Name
 }
 
+func (unit *HelmExecUnit) AddUnitsEnvironmentVariables(eu *core.ExecutionUnit) (values []Value, err error) {
+	if unit.Deployment != nil {
+		v, err := unit.addEnvsVarToDeployment(eu.EnvironmentVariables)
+		if err != nil {
+			return nil, err
+		}
+
+		values = append(values, v...)
+	} else if unit.Pod != nil {
+		v, err := unit.addEnvVarToPod(eu.EnvironmentVariables)
+		if err != nil {
+			return nil, err
+		}
+
+		values = append(values, v...)
+	}
+	return
+}
 func (unit *HelmExecUnit) addEnvsVarToDeployment(envVars core.EnvironmentVariables) ([]Value, error) {
 	values := []Value{}
 
diff --git a/pkg/infra/kubernetes/helm_chart.go b/pkg/infra/kubernetes/helm_chart.go
index b9538308..f511f1af 100644
--- a/pkg/infra/kubernetes/helm_chart.go
+++ b/pkg/infra/kubernetes/helm_chart.go
@@ -194,11 +194,11 @@ func (chart *KlothoHelmChart) handleExecutionUnit(unit *HelmExecUnit, eu *core.E
 	}
 	values = append(values, upstreamValues...)
 
-	persistValues, err := unit.handlePersistForExecUnit(deps)
+	unitEnvValues, err := unit.AddUnitsEnvironmentVariables(eu)
 	if err != nil {
 		return nil, err
 	}
-	values = append(values, persistValues...)
+	values = append(values, unitEnvValues...)
 
 	return values, nil
 }
diff --git a/pkg/infra/kubernetes/persist.go b/pkg/infra/kubernetes/persist.go
deleted file mode 100644
index 2834f10d..00000000
--- a/pkg/infra/kubernetes/persist.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package kubernetes
-
-import (
-	"github.com/klothoplatform/klotho/pkg/core"
-)
-
-type PersistEnvVars string
-
-func (unit *HelmExecUnit) handlePersistForExecUnit(deps *core.Dependencies) ([]Value, error) {
-	var values []Value
-	envVars := generateEnvVars(deps, unit.Name)
-
-	if len(envVars) == 0 {
-		return values, nil
-	}
-
-	if unit.Deployment != nil {
-		v, err := unit.addEnvsVarToDeployment(envVars)
-		if err != nil {
-			return nil, err
-		}
-
-		values = append(values, v...)
-	} else if unit.Pod != nil {
-		v, err := unit.addEnvVarToPod(envVars)
-		if err != nil {
-			return nil, err
-		}
-
-		values = append(values, v...)
-	}
-
-	return values, nil
-}
-
-func generateEnvVars(deps *core.Dependencies, name string) core.EnvironmentVariables {
-	envVars := core.EnvironmentVariables{}
-	for _, target := range deps.Downstream(core.ResourceKey{Name: name, Kind: core.ExecutionUnitKind}) {
-		switch target.Kind {
-		case string(core.PersistORMKind):
-			envVars = append(envVars, core.GenerateOrmConnStringEnvVar(target.Name))
-		case string(core.PersistRedisNodeKind):
-			envVars = append(
-				envVars,
-				core.GenerateRedisHostEnvVar(target.Name, target.Kind),
-				core.GenerateRedisPortEnvVar(target.Name, target.Kind),
-			)
-		case string(core.PersistRedisClusterKind):
-			envVars = append(
-				envVars,
-				core.GenerateRedisHostEnvVar(target.Name, target.Kind),
-				core.GenerateRedisPortEnvVar(target.Name, target.Kind),
-			)
-		}
-	}
-	return envVars
-}
diff --git a/pkg/infra/pulumi_aws/deploylib.ts b/pkg/infra/pulumi_aws/deploylib.ts
index 8b099f16..7a527af6 100755
--- a/pkg/infra/pulumi_aws/deploylib.ts
+++ b/pkg/infra/pulumi_aws/deploylib.ts
@@ -29,6 +29,18 @@ export enum Resource {
     config = 'config',
 }
 
+export interface Gateway {
+    Name: string
+    Routes: Route[]
+    ApiType: 'REST' | 'HTTP'
+}
+
+export interface Route {
+    verb: string
+    path: string
+    execUnitName: string
+}
+
 export interface ResourceKey {
     Kind: string
     Name: string
diff --git a/pkg/infra/pulumi_aws/iac/api_gateway.ts b/pkg/infra/pulumi_aws/iac/api_gateway.ts
index 5c99de47..72700874 100644
--- a/pkg/infra/pulumi_aws/iac/api_gateway.ts
+++ b/pkg/infra/pulumi_aws/iac/api_gateway.ts
@@ -1,22 +1,10 @@
 import * as aws from '@pulumi/aws'
 import * as pulumi from '@pulumi/pulumi'
-import { CloudCCLib } from '../deploylib'
+import { CloudCCLib, Route, Gateway } from '../deploylib'
 import * as sha256 from 'simple-sha256'
 import { LoadBalancerPlugin } from './load_balancing'
 import { DeploymentArgs, StageArgs } from '@pulumi/aws/apigatewayv2'
 
-export interface Route {
-    verb: string
-    path: string
-    execUnitName: string
-}
-
-export interface Gateway {
-    Name: string
-    Routes: Route[]
-    ApiType: 'REST' | 'HTTP'
-}
-
 function sanitizeName(g: Gateway) {
     return g.Name.replace(/[^a-zA-Z0-9_-]+/g, '-')
 }
diff --git a/pkg/infra/pulumi_aws/iac/load_balancing.ts b/pkg/infra/pulumi_aws/iac/load_balancing.ts
index 98562e15..b51892a1 100644
--- a/pkg/infra/pulumi_aws/iac/load_balancing.ts
+++ b/pkg/infra/pulumi_aws/iac/load_balancing.ts
@@ -9,18 +9,7 @@ import {
 } from '@pulumi/aws/lb'
 import { ListenerRuleArgs } from '@pulumi/aws/alb'
 import { hash as h, sanitized } from './sanitization/sanitizer'
-import { CloudCCLib } from '../deploylib'
-
-export interface Route {
-    verb: string
-    path: string
-    execUnitName: string
-}
-
-export interface Gateway {
-    Name: string
-    Routes: Route[]
-}
+import { CloudCCLib, Gateway } from '../deploylib'
 
 export class LoadBalancerPlugin {
     // A map of all resources which are going to be fronted by a load balancer
diff --git a/pkg/input/readdir.go b/pkg/input/readdir.go
index a32b44d2..60656771 100644
--- a/pkg/input/readdir.go
+++ b/pkg/input/readdir.go
@@ -3,8 +3,6 @@ package input
 import (
 	"encoding/json"
 	"fmt"
-	"github.com/klothoplatform/klotho/pkg/filter/predicate"
-	"github.com/klothoplatform/klotho/pkg/lang/csharp/csproj"
 	"io"
 	"io/fs"
 	"os"
@@ -12,6 +10,9 @@ import (
 	"path/filepath"
 	"strings"
 
+	"github.com/klothoplatform/klotho/pkg/filter/predicate"
+	"github.com/klothoplatform/klotho/pkg/lang/csharp/csproj"
+
 	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/lang/csharp"
@@ -226,10 +227,10 @@ func ReadDir(fsys fs.FS, cfg config.Application, cfgFilePath string) (*core.Inpu
 				if tsConfig.CompilerOptions.OutDir != "" {
 					tsPrefix := tsConfig.CompilerOptions.OutDir + string(os.PathSeparator)
 					newPath := strings.TrimPrefix(path, tsPrefix) // tsPrefix is already joined to cfg.Path, so use `path` not `relPath`
-					if relPath != newPath {
+					if relPath != newPath && newPath != path {
 						zap.S().Debugf("Removing TS outdir from %s -> %s", relPath, newPath)
+						relPath = newPath
 					}
-					relPath = newPath
 				}
 				f, err = addFile(fsys, path, relPath, javascript.NewFile)
 				jsLang.foundSources = true
diff --git a/pkg/lang/javascript/aws_runtime/aws.go b/pkg/lang/javascript/aws_runtime/aws.go
index 446b2b89..9eb0e35e 100644
--- a/pkg/lang/javascript/aws_runtime/aws.go
+++ b/pkg/lang/javascript/aws_runtime/aws.go
@@ -5,11 +5,12 @@ import (
 	"embed"
 	"encoding/json"
 	"fmt"
-	"github.com/klothoplatform/klotho/pkg/sanitization"
 	"path/filepath"
 	"regexp"
 	"strings"
 
+	"github.com/klothoplatform/klotho/pkg/sanitization"
+
 	"github.com/klothoplatform/klotho/pkg/config"
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/lang/javascript"
@@ -161,7 +162,7 @@ func (r *AwsRuntime) AddFsRuntimeFiles(unit *core.ExecutionUnit, bucketNameEnvVa
 	if err != nil {
 		return err
 	}
-	err = javascript.AddRuntimeFile(unit, templateData, fmt.Sprintf("fs_%s.js.tmpl", id), content)
+	err = javascript.AddRuntimeFile(unit, templateData, fmt.Sprintf("fs_%s.js.tmpl", sanitization.IdentifierSanitizer.Apply(id)), content)
 	return err
 }
 


Test patch:
diff --git a/.github/workflows/run-integ-tests.yaml b/.github/workflows/run-integ-tests.yaml
index 9ecf614a..d8b10530 100644
--- a/.github/workflows/run-integ-tests.yaml
+++ b/.github/workflows/run-integ-tests.yaml
@@ -97,7 +97,7 @@ jobs:
         uses: actions/checkout@v3
       - uses: actions/setup-go@v3
         with:
-          go-version: '1.19.6'
+          go-version: '1.20.2'
       - name: pre-build
         if: ${{ inputs.pre-build-script }}
         run: ${{ inputs.pre-build-script }}
@@ -131,7 +131,7 @@ jobs:
           set -x
           cd _test_resources
           npm i
-          pulumi -s integ-test-shared stack select --create
+          pulumi -s integ-test-shared-${{ env.AWS_REGION }} stack select --create
           pulumi stack tag set usage integ-test
           pulumi config refresh || true
           pulumi up -y --refresh
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 5ec80ad3..531c1475 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -13,7 +13,7 @@ jobs:
     steps:
       - uses: actions/setup-go@v3
         with:
-          go-version: '1.19.6'
+          go-version: '1.20.2'
       - uses: actions/checkout@v3
       - uses: actions/cache@v2
         with:
@@ -27,19 +27,4 @@ jobs:
             ${{ runner.os }}-go-
 
       - name: Run test
-        run: go test -race -v -coverprofile=coverage.out ./...
-
-      - name: Convert to Cobertura
-        run: |
-          go get github.com/t-yuki/gocover-cobertura
-          go install github.com/t-yuki/gocover-cobertura
-          gocover-cobertura < coverage.out > coverage.xml
-
-      - name: Code Coverage Summary Report
-        uses: irongut/CodeCoverageSummary@v1.2.0
-        with:
-          filename: coverage.xml
-          format: markdown
-          output: both
-          hide_complexity: true
-          hide_branch_rate: true
+        run: go test -race -v ./...
diff --git a/pkg/infra/kubernetes/exec_unit_test.go b/pkg/infra/kubernetes/exec_unit_test.go
index fd77bb68..859ce57e 100644
--- a/pkg/infra/kubernetes/exec_unit_test.go
+++ b/pkg/infra/kubernetes/exec_unit_test.go
@@ -6,6 +6,7 @@ import (
 
 	"github.com/klothoplatform/klotho/pkg/core"
 	"github.com/klothoplatform/klotho/pkg/lang/dockerfile"
+	"github.com/klothoplatform/klotho/pkg/lang/yaml"
 	yamlLang "github.com/klothoplatform/klotho/pkg/lang/yaml"
 	"github.com/stretchr/testify/assert"
 )
@@ -627,6 +628,244 @@ spec:
 	}
 }
 
+func Test_addUnitsEnvironmentVariables(t *testing.T) {
+	type testResult struct {
+		values []Value
+		file   string
+	}
+	tests := []struct {
+		name           string
+		unit           HelmExecUnit
+		deploymentYaml string
+		podYaml        string
+		deps           []core.Dependency
+		want           testResult
+		wantErr        bool
+	}{
+		{
+			name: "unit with deployment",
+			unit: HelmExecUnit{
+				Name:      "unit",
+				Namespace: "default",
+			},
+			deploymentYaml: `apiVersion: apps/v1
+kind: Deployment
+metadata:
+  creationTimestamp: null
+  labels:
+    execUnit: testUnit
+  name: nginx-deployment
+spec:
+  replicas: 3
+  selector:
+    matchLabels:
+      app: nginx
+      execUnit: testUnit
+  strategy: {}
+  template:
+    metadata:
+      creationTimestamp: null
+      labels:
+        app: nginx
+        execUnit: testUnit
+    spec:
+      containers:
+      - image: '{{ .Values.testUnitImage }}'
+        name: nginx
+        resources: {}
+      serviceAccountName: testUnit
+status: {}
+`,
+			want: testResult{
+				file: `apiVersion: apps/v1
+kind: Deployment
+metadata:
+  creationTimestamp: null
+  labels:
+    execUnit: testUnit
+  name: nginx-deployment
+spec:
+  replicas: 3
+  selector:
+    matchLabels:
+      app: nginx
+      execUnit: testUnit
+  strategy: {}
+  template:
+    metadata:
+      creationTimestamp: null
+      labels:
+        app: nginx
+        execUnit: testUnit
+    spec:
+      containers:
+      - env:
+        - name: TESTBUCKET_BUCKET_NAME
+          value: '{{ .Values.TESTBUCKETBUCKETNAME }}'
+        - name: TESTREDIS_PERSIST_REDIS_HOST
+          value: '{{ .Values.TESTREDISPERSISTREDISHOST }}'
+        - name: TESTSECRET_CONFIG_SECRET
+          value: '{{ .Values.TESTSECRETCONFIGSECRET }}'
+        - name: TESTORM_PERSIST_ORM_CONNECTION
+          value: '{{ .Values.TESTORMPERSISTORMCONNECTION }}'
+        image: '{{ .Values.testUnitImage }}'
+        name: nginx
+        resources: {}
+      serviceAccountName: testUnit
+status: {}
+`,
+				values: []Value{
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Deployment",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTREDISPERSISTREDISHOST",
+						EnvironmentVariable: core.GenerateRedisHostEnvVar("testRedis", string(core.PersistRedisClusterKind)),
+					},
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Deployment",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTBUCKETBUCKETNAME",
+						EnvironmentVariable: core.GenerateBucketEnvVar("testBucket"),
+					},
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Deployment",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTORMPERSISTORMCONNECTION",
+						EnvironmentVariable: core.GenerateOrmConnStringEnvVar("testOrm"),
+					},
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Deployment",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTSECRETCONFIGSECRET",
+						EnvironmentVariable: core.GenerateSecretEnvVar("testSecret", core.ConfigKind),
+					},
+				},
+			},
+		},
+		{
+			name: "unit with pod",
+			unit: HelmExecUnit{
+				Name:      "unit",
+				Namespace: "default",
+			},
+			podYaml: `apiVersion: v1
+kind: Pod
+metadata:
+  name: test
+spec:
+  containers:
+  - name: web
+    image: nginx`,
+			want: testResult{
+				file: `apiVersion: v1
+kind: Pod
+metadata:
+  creationTimestamp: null
+  name: test
+spec:
+  containers:
+  - env:
+    - name: TESTBUCKET_BUCKET_NAME
+      value: '{{ .Values.TESTBUCKETBUCKETNAME }}'
+    - name: TESTREDIS_PERSIST_REDIS_HOST
+      value: '{{ .Values.TESTREDISPERSISTREDISHOST }}'
+    - name: TESTSECRET_CONFIG_SECRET
+      value: '{{ .Values.TESTSECRETCONFIGSECRET }}'
+    - name: TESTORM_PERSIST_ORM_CONNECTION
+      value: '{{ .Values.TESTORMPERSISTORMCONNECTION }}'
+    image: nginx
+    name: web
+    resources: {}
+status: {}
+`,
+				values: []Value{
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Pod",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTREDISPERSISTREDISHOST",
+						EnvironmentVariable: core.GenerateRedisHostEnvVar("testRedis", string(core.PersistRedisClusterKind)),
+					},
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Pod",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTBUCKETBUCKETNAME",
+						EnvironmentVariable: core.GenerateBucketEnvVar("testBucket"),
+					},
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Pod",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTORMPERSISTORMCONNECTION",
+						EnvironmentVariable: core.GenerateOrmConnStringEnvVar("testOrm"),
+					},
+					{
+						ExecUnitName:        "unit",
+						Kind:                "Pod",
+						Type:                string(EnvironmentVariableTransformation),
+						Key:                 "TESTSECRETCONFIGSECRET",
+						EnvironmentVariable: core.GenerateSecretEnvVar("testSecret", core.ConfigKind),
+					},
+				},
+			},
+		},
+		{
+			name: "has dependency but no pod or deployment file",
+			unit: HelmExecUnit{
+				Name:      "unit",
+				Namespace: "default",
+			},
+			want: testResult{
+				values: []Value{},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert := assert.New(t)
+
+			eunit := core.ExecutionUnit{}
+			eunit.EnvironmentVariables.Add(core.GenerateBucketEnvVar("testBucket"))
+			eunit.EnvironmentVariables.Add(core.GenerateRedisHostEnvVar("testRedis", string(core.PersistRedisClusterKind)))
+			eunit.EnvironmentVariables.Add(core.GenerateSecretEnvVar("testSecret", core.ConfigKind))
+			eunit.EnvironmentVariables.Add(core.GenerateOrmConnStringEnvVar("testOrm"))
+
+			if tt.deploymentYaml != "" {
+				f, err := yaml.NewFile("deployment.yaml", strings.NewReader(tt.deploymentYaml))
+				assert.NoError(err)
+				tt.unit.Deployment = f
+			}
+			if tt.podYaml != "" {
+				f, err := yaml.NewFile("pod.yaml", strings.NewReader(tt.podYaml))
+				assert.NoError(err)
+				tt.unit.Pod = f
+			}
+
+			values, err := tt.unit.AddUnitsEnvironmentVariables(&eunit)
+			if tt.wantErr {
+				assert.Error(err)
+				return
+			} else if !assert.NoError(err) {
+				return
+
+			}
+			assert.ElementsMatch(tt.want.values, values)
+
+			if tt.deploymentYaml != "" {
+				assert.Equal(tt.want.file, string(tt.unit.Deployment.Program()))
+			}
+			if tt.podYaml != "" {
+				assert.Equal(tt.want.file, string(tt.unit.Pod.Program()))
+			}
+		})
+	}
+}
+
 func Test_transformServiceAccount(t *testing.T) {
 	type result struct {
 		values  []Value
diff --git a/pkg/infra/kubernetes/persist_test.go b/pkg/infra/kubernetes/persist_test.go
deleted file mode 100644
index 45e8eaf5..00000000
--- a/pkg/infra/kubernetes/persist_test.go
+++ /dev/null
@@ -1,335 +0,0 @@
-package kubernetes
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/klothoplatform/klotho/pkg/core"
-	"github.com/klothoplatform/klotho/pkg/lang/yaml"
-	"github.com/stretchr/testify/assert"
-)
-
-func Test_handlePersistForExecUnit(t *testing.T) {
-	type testResult struct {
-		values []Value
-		file   string
-	}
-	tests := []struct {
-		name           string
-		unit           HelmExecUnit
-		deploymentYaml string
-		podYaml        string
-		deps           []core.Dependency
-		want           testResult
-		wantErr        bool
-	}{
-		{
-			name: "orm dependency and deployment",
-			unit: HelmExecUnit{
-				Name:      "unit",
-				Namespace: "default",
-			},
-			deploymentYaml: `apiVersion: apps/v1
-kind: Deployment
-metadata:
-  creationTimestamp: null
-  labels:
-    execUnit: testUnit
-  name: nginx-deployment
-spec:
-  replicas: 3
-  selector:
-    matchLabels:
-      app: nginx
-      execUnit: testUnit
-  strategy: {}
-  template:
-    metadata:
-      creationTimestamp: null
-      labels:
-        app: nginx
-        execUnit: testUnit
-    spec:
-      containers:
-      - image: '{{ .Values.testUnitImage }}'
-        name: nginx
-        resources: {}
-      serviceAccountName: testUnit
-status: {}
-`,
-			deps: []core.Dependency{
-				{
-					Source: core.ResourceKey{Kind: core.ExecutionUnitKind, Name: "unit"},
-					Target: core.ResourceKey{Kind: string(core.PersistORMKind), Name: "unit"},
-				},
-			},
-			want: testResult{
-				file: `apiVersion: apps/v1
-kind: Deployment
-metadata:
-  creationTimestamp: null
-  labels:
-    execUnit: testUnit
-  name: nginx-deployment
-spec:
-  replicas: 3
-  selector:
-    matchLabels:
-      app: nginx
-      execUnit: testUnit
-  strategy: {}
-  template:
-    metadata:
-      creationTimestamp: null
-      labels:
-        app: nginx
-        execUnit: testUnit
-    spec:
-      containers:
-      - env:
-        - name: UNIT_PERSIST_ORM_CONNECTION
-          value: '{{ .Values.UNITPERSISTORMCONNECTION }}'
-        image: '{{ .Values.testUnitImage }}'
-        name: nginx
-        resources: {}
-      serviceAccountName: testUnit
-status: {}
-`,
-				values: []Value{
-					{
-						ExecUnitName:        "unit",
-						Kind:                "Deployment",
-						Type:                string(EnvironmentVariableTransformation),
-						Key:                 "UNITPERSISTORMCONNECTION",
-						EnvironmentVariable: core.NewEnvironmentVariable("UNIT_PERSIST_ORM_CONNECTION", "persist_orm", "unit", "connection_string"),
-					},
-				},
-			},
-		},
-		{
-			name: "redis node dependency and pod",
-			unit: HelmExecUnit{
-				Name:      "unit",
-				Namespace: "default",
-			},
-			podYaml: `apiVersion: v1
-kind: Pod
-metadata:
-  name: test
-spec:
-  containers:
-  - name: web
-    image: nginx`,
-			deps: []core.Dependency{
-				{
-					Source: core.ResourceKey{Kind: core.ExecutionUnitKind, Name: "unit"},
-					Target: core.ResourceKey{Kind: string(core.PersistRedisNodeKind), Name: "unit"},
-				},
-			},
-			want: testResult{
-				file: `apiVersion: v1
-kind: Pod
-metadata:
-  creationTimestamp: null
-  name: test
-spec:
-  containers:
-  - env:
-    - name: UNIT_PERSIST_REDIS_HOST
-      value: '{{ .Values.UNITPERSISTREDISHOST }}'
-    - name: UNIT_PERSIST_REDIS_PORT
-      value: '{{ .Values.UNITPERSISTREDISPORT }}'
-    image: nginx
-    name: web
-    resources: {}
-status: {}
-`,
-				values: []Value{
-					{
-						ExecUnitName:        "unit",
-						Kind:                "Pod",
-						Type:                string(EnvironmentVariableTransformation),
-						Key:                 "UNITPERSISTREDISHOST",
-						EnvironmentVariable: core.NewEnvironmentVariable("UNIT_PERSIST_REDIS_HOST", "persist_redis_node", "unit", "host"),
-					},
-					{
-						ExecUnitName:        "unit",
-						Kind:                "Pod",
-						Type:                string(EnvironmentVariableTransformation),
-						Key:                 "UNITPERSISTREDISPORT",
-						EnvironmentVariable: core.NewEnvironmentVariable("UNIT_PERSIST_REDIS_PORT", "persist_redis_node", "unit", "port"),
-					},
-				},
-			},
-		},
-		{
-			name: "redis cluster dependency and pod",
-			unit: HelmExecUnit{
-				Name:      "unit",
-				Namespace: "default",
-			},
-			podYaml: `apiVersion: v1
-kind: Pod
-metadata:
-  name: test
-spec:
-  containers:
-  - name: web
-    image: nginx`,
-			deps: []core.Dependency{
-				{
-					Source: core.ResourceKey{Kind: core.ExecutionUnitKind, Name: "unit"},
-					Target: core.ResourceKey{Kind: string(core.PersistRedisClusterKind), Name: "unit"},
-				},
-			},
-			want: testResult{
-				file: `apiVersion: v1
-kind: Pod
-metadata:
-  creationTimestamp: null
-  name: test
-spec:
-  containers:
-  - env:
-    - name: UNIT_PERSIST_REDIS_HOST
-      value: '{{ .Values.UNITPERSISTREDISHOST }}'
-    - name: UNIT_PERSIST_REDIS_PORT
-      value: '{{ .Values.UNITPERSISTREDISPORT }}'
-    image: nginx
-    name: web
-    resources: {}
-status: {}
-`,
-				values: []Value{
-					{
-						ExecUnitName:        "unit",
-						Kind:                "Pod",
-						Type:                string(EnvironmentVariableTransformation),
-						Key:                 "UNITPERSISTREDISHOST",
-						EnvironmentVariable: core.NewEnvironmentVariable("UNIT_PERSIST_REDIS_HOST", "persist_redis_cluster", "unit", "host"),
-					},
-					{
-						ExecUnitName:        "unit",
-						Kind:                "Pod",
-						Type:                string(EnvironmentVariableTransformation),
-						Key:                 "UNITPERSISTREDISPORT",
-						EnvironmentVariable: core.NewEnvironmentVariable("UNIT_PERSIST_REDIS_PORT", "persist_redis_cluster", "unit", "port"),
-					},
-				},
-			},
-		},
-		{
-			name: "has dependency but no pod or deployment file",
-			unit: HelmExecUnit{
-				Name:      "unit",
-				Namespace: "default",
-			},
-			deps: []core.Dependency{
-				{
-					Source: core.ResourceKey{Kind: core.ExecutionUnitKind, Name: "unit"},
-					Target: core.ResourceKey{Kind: string(core.PersistRedisClusterKind), Name: "unit"},
-				},
-			},
-		},
-		{
-			name: "no deps",
-			unit: HelmExecUnit{
-				Name:      "unit",
-				Namespace: "default",
-			},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			assert := assert.New(t)
-
-			deps := &core.Dependencies{}
-			for _, dep := range tt.deps {
-				deps.Add(dep.Source, dep.Target)
-			}
-
-			if tt.deploymentYaml != "" {
-				f, err := yaml.NewFile("deployment.yaml", strings.NewReader(tt.deploymentYaml))
-				assert.NoError(err)
-				tt.unit.Deployment = f
-			}
-			if tt.podYaml != "" {
-				f, err := yaml.NewFile("pod.yaml", strings.NewReader(tt.podYaml))
-				assert.NoError(err)
-				tt.unit.Pod = f
-			}
-
-			values, err := tt.unit.handlePersistForExecUnit(deps)
-			if tt.wantErr {
-				assert.Error(err)
-				return
-			} else if !assert.NoError(err) {
-				return
-
-			}
-			assert.Equal(tt.want.values, values)
-
-			if tt.deploymentYaml != "" {
-				assert.Equal(tt.want.file, string(tt.unit.Deployment.Program()))
-			}
-			if tt.podYaml != "" {
-				assert.Equal(tt.want.file, string(tt.unit.Pod.Program()))
-			}
-		})
-	}
-}
-
-func Test_generateEnvVarsForPersist(t *testing.T) {
-	tests := []struct {
-		name     string
-		unit     *core.ExecutionUnit
-		resource core.CloudResource
-		values   core.EnvironmentVariables
-	}{
-		{
-			name:     "Wrong dependencies",
-			unit:     &core.ExecutionUnit{Name: "main"},
-			resource: &core.Persist{Name: "file", Kind: core.PersistFileKind},
-			values:   core.EnvironmentVariables{},
-		},
-		{
-			name:     "orm dependency",
-			unit:     &core.ExecutionUnit{Name: "main"},
-			resource: &core.Persist{Name: "orm", Kind: core.PersistORMKind},
-			values:   core.EnvironmentVariables{{Name: "ORM_PERSIST_ORM_CONNECTION", Kind: "persist_orm", ResourceID: "orm", Value: string(core.CONNECTION_STRING)}},
-		},
-		{
-			name:     "redis node dependency",
-			unit:     &core.ExecutionUnit{Name: "main"},
-			resource: &core.Persist{Name: "redisNode", Kind: core.PersistRedisNodeKind},
-			values: core.EnvironmentVariables{
-				{Name: "REDISNODE_PERSIST_REDIS_HOST", Kind: "persist_redis_node", ResourceID: "redisNode", Value: string(core.HOST)},
-				{Name: "REDISNODE_PERSIST_REDIS_PORT", Kind: "persist_redis_node", ResourceID: "redisNode", Value: string(core.PORT)},
-			},
-		},
-		{
-			name:     "redis cluster dependency",
-			unit:     &core.ExecutionUnit{Name: "main"},
-			resource: &core.Persist{Name: "redisCluster", Kind: core.PersistRedisClusterKind},
-			values: core.EnvironmentVariables{
-				{Name: "REDISCLUSTER_PERSIST_REDIS_HOST", Kind: "persist_redis_cluster", ResourceID: "redisCluster", Value: string(core.HOST)},
-				{Name: "REDISCLUSTER_PERSIST_REDIS_PORT", Kind: "persist_redis_cluster", ResourceID: "redisCluster", Value: string(core.PORT)},
-			},
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-
-			assert := assert.New(t)
-
-			results := &core.CompilationResult{}
-			results.Add(tt.resource)
-
-			deps := &core.Dependencies{}
-			deps.Add(tt.unit.Key(), tt.resource.Key())
-			envVars := generateEnvVars(deps, tt.unit.Name)
-
-			assert.Equal(tt.values, envVars)
-		})
-	}
-}