Repository: linkerd/linkerd2
Pull request #5278 (2020-11-24 12:08:36)

extension: Add new jaeger binary
Depends on #5275 

This branch adds a new jaeger binary project in the jaeger directory.
This follows the same logic as that of `linkerd install`. But as
`linkerd install` VFS logic expects charts to be present in `/charts`
directory, This command gets its own static pkg to generate its own
VFS for its chart.

Signed-off-by: Tarun Pothulapati <tarunpothulapati@outlook.com>



Patch:
diff --git a/bin/build-cli-bin b/bin/build-cli-bin
index fa955bbee..fbe234960 100755
--- a/bin/build-cli-bin
+++ b/bin/build-cli-bin
@@ -17,7 +17,9 @@ rootdir=$( cd "$bindir"/.. && pwd )
     cd "$rootdir"
     cd "$(pwd -P)"
     target=target/cli/$(os)/linkerd
-    GO111MODULE=on go generate -mod=readonly ./pkg/charts/static # TODO: `go generate` does not honor -mod=readonly
+     # TODO: `go generate` does not honor -mod=readonly
+    GO111MODULE=on go generate -mod=readonly ./pkg/charts/static
+    GO111MODULE=on go generate -mod=readonly ./jaeger/static
     root_tag=$("$bindir"/root-tag)
     GO111MODULE=on CGO_ENABLED=0 go build -o $target -tags prod -mod=readonly -ldflags "-s -w -X github.com/linkerd/linkerd2/pkg/version.Version=$root_tag" ./cli
     echo "$target"
diff --git a/cli/Dockerfile-bin b/cli/Dockerfile-bin
index 44aae6081..7deb9cf57 100644
--- a/cli/Dockerfile-bin
+++ b/cli/Dockerfile-bin
@@ -14,6 +14,7 @@ FROM go-deps as golang-single-arch
 WORKDIR /linkerd-build
 COPY cli cli
 COPY charts charts
+COPY jaeger jaeger
 
 COPY controller/k8s controller/k8s
 COPY controller/api controller/api
@@ -24,6 +25,7 @@ RUN mkdir -p /out
 # Generate static templates
 # TODO: `go generate` does not honor -mod=readonly
 RUN go generate -mod=readonly ./pkg/charts/static
+RUN go generate -mod=readonly ./jaeger/static
 
 # Cache builds without version info
 RUN CGO_ENABLED=0 GOOS=darwin go build -o /out/linkerd-darwin -tags prod -mod=readonly -ldflags "-s -w" ./cli
diff --git a/cli/cmd/install-cni-plugin.go b/cli/cmd/install-cni-plugin.go
index 7b7170757..9e2e181ba 100644
--- a/cli/cmd/install-cni-plugin.go
+++ b/cli/cmd/install-cni-plugin.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/linkerd/linkerd2/pkg/charts"
 	cnicharts "github.com/linkerd/linkerd2/pkg/charts/cni"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/version"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
@@ -217,6 +218,7 @@ func renderCNIPlugin(w io.Writer, config *cniPluginOptions) error {
 		Namespace: controlPlaneNamespace,
 		RawValues: rawValues,
 		Files:     files,
+		Fs:        static.Templates,
 	}
 	buf, err := chart.RenderCNI()
 	if err != nil {
diff --git a/cli/cmd/install.go b/cli/cmd/install.go
index e54afeee1..3dcbfda78 100644
--- a/cli/cmd/install.go
+++ b/cli/cmd/install.go
@@ -12,6 +12,7 @@ import (
 	"github.com/linkerd/linkerd2/cli/flag"
 	"github.com/linkerd/linkerd2/pkg/charts"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/tree"
@@ -331,6 +332,7 @@ func render(w io.Writer, values *l5dcharts.Values, stage string) error {
 					Name: chartutil.ValuesfileName,
 				},
 			},
+			Fs: static.Templates,
 		}
 	}
 
@@ -368,6 +370,7 @@ func render(w io.Writer, values *l5dcharts.Values, stage string) error {
 		Namespace: controlPlaneNamespace,
 		RawValues: rawValues,
 		Files:     files,
+		Fs:        static.Templates,
 	}
 	buf, err := chart.Render()
 	if err != nil {
diff --git a/cli/cmd/multicluster.go b/cli/cmd/multicluster.go
index 94a6c33ec..04155a99f 100644
--- a/cli/cmd/multicluster.go
+++ b/cli/cmd/multicluster.go
@@ -14,6 +14,7 @@ import (
 	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"github.com/linkerd/linkerd2/pkg/charts/multicluster"
 	mccharts "github.com/linkerd/linkerd2/pkg/charts/multicluster"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/healthcheck"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	mc "github.com/linkerd/linkerd2/pkg/multicluster"
@@ -281,6 +282,7 @@ func newAllowCommand() *cobra.Command {
 				Namespace: controlPlaneNamespace,
 				RawValues: rawValues,
 				Files:     files,
+				Fs:        static.Templates,
 			}
 			buf, err := chart.RenderNoPartials()
 			if err != nil {
@@ -372,6 +374,7 @@ func newMulticlusterInstallCommand() *cobra.Command {
 				Namespace: controlPlaneNamespace,
 				RawValues: rawValues,
 				Files:     files,
+				Fs:        static.Templates,
 			}
 			buf, err := chart.RenderNoPartials()
 			if err != nil {
@@ -476,6 +479,7 @@ func newMulticlusterUninstallCommand() *cobra.Command {
 				Namespace: controlPlaneNamespace,
 				RawValues: rawValues,
 				Files:     files,
+				Fs:        static.Templates,
 			}
 			buf, err := chart.RenderNoPartials()
 			if err != nil {
@@ -695,6 +699,7 @@ func newLinkCommand() *cobra.Command {
 				Namespace: controlPlaneNamespace,
 				RawValues: rawValues,
 				Files:     files,
+				Fs:        static.Templates,
 			}
 			serviceMirrorOut, err := chart.RenderNoPartials()
 			if err != nil {
diff --git a/cli/cmd/root.go b/cli/cmd/root.go
index c3d31ad17..04ad8aa30 100644
--- a/cli/cmd/root.go
+++ b/cli/cmd/root.go
@@ -8,13 +8,13 @@ import (
 	"strings"
 	"time"
 
-	corev1 "k8s.io/api/core/v1"
-	"k8s.io/client-go/tools/clientcmd"
-
 	"github.com/fatih/color"
 	"github.com/linkerd/linkerd2/cli/flag"
+	jaeger "github.com/linkerd/linkerd2/jaeger/cmd"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/client-go/tools/clientcmd"
 )
 
 const (
@@ -128,6 +128,9 @@ func init() {
 	RootCmd.AddCommand(newCmdVersion())
 	RootCmd.AddCommand(newCmdMulticluster())
 	RootCmd.AddCommand(newCmdUninstall())
+
+	// Extension Sub Commands
+	RootCmd.AddCommand(jaeger.NewCmdJaeger())
 }
 
 type statOptionsBase struct {
diff --git a/jaeger/cmd/install.go b/jaeger/cmd/install.go
new file mode 100644
index 000000000..79915a5ba
--- /dev/null
+++ b/jaeger/cmd/install.go
@@ -0,0 +1,139 @@
+package cmd
+
+import (
+	"context"
+	"fmt"
+	"io"
+	"os"
+
+	"github.com/linkerd/linkerd2/jaeger/static"
+	jaeger "github.com/linkerd/linkerd2/jaeger/values"
+	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/pkg/healthcheck"
+	"github.com/linkerd/linkerd2/pkg/k8s"
+	"github.com/spf13/cobra"
+	"helm.sh/helm/v3/pkg/chart/loader"
+	"helm.sh/helm/v3/pkg/chartutil"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"sigs.k8s.io/yaml"
+)
+
+var (
+	templatesJaeger = []string{
+		"templates/namespace.yaml",
+		"templates/rbac.yaml",
+		"templates/tracing.yaml",
+	}
+)
+
+func newCmdInstall() *cobra.Command {
+	var skipChecks bool
+
+	values, err := jaeger.NewValues()
+	if err != nil {
+		fmt.Fprint(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+
+	cmd := &cobra.Command{
+		Use:   "install [flags]",
+		Args:  cobra.NoArgs,
+		Short: "Output Kubernetes resources to install jaeger extension",
+		Long:  `Output Kubernetes resources to install jaeger extension.`,
+		Example: `  # Default install.
+  linkerd jaeger install | kubectl apply -f -
+  # Install Jaeger extension into a non-default namespace.
+  linkerd jaeger install --namespace custom | kubectl apply -f -`,
+		RunE: func(cmd *cobra.Command, args []string) error {
+			if !skipChecks {
+				// Ensure there is a Linkerd installation.
+				exists, err := checkIfLinkerdExists(cmd.Context())
+				if err != nil {
+					return fmt.Errorf("could not check for Linkerd existence: %s", err)
+				}
+
+				if !exists {
+					return fmt.Errorf("could not find a Linkerd installation")
+				}
+			}
+
+			return install(os.Stdout, values)
+		},
+	}
+
+	cmd.Flags().BoolVar(
+		&skipChecks, "skip-checks", false,
+		`Skip checks for namespace existence`,
+	)
+
+	// TODO: Add --set flag set and also config
+
+	return cmd
+}
+
+func install(w io.Writer, values *jaeger.Values) error {
+
+	// TODO: Add any validation logic here
+
+	return render(w, values)
+}
+
+func render(w io.Writer, values *jaeger.Values) error {
+	// Render raw values and create chart config
+	rawValues, err := yaml.Marshal(values)
+	if err != nil {
+		return err
+	}
+
+	files := []*loader.BufferedFile{
+		{Name: chartutil.ChartfileName},
+	}
+
+	for _, template := range templatesJaeger {
+		files = append(files,
+			&loader.BufferedFile{Name: template},
+		)
+	}
+
+	chart := &charts.Chart{
+		Name:      "jaeger",
+		Dir:       "jaeger",
+		Namespace: values.Namespace,
+		RawValues: rawValues,
+		Files:     files,
+		Fs:        static.Templates,
+	}
+	buf, err := chart.Render()
+	if err != nil {
+		return err
+	}
+
+	_, err = w.Write(buf.Bytes())
+	return err
+}
+
+func checkIfLinkerdExists(ctx context.Context) (bool, error) {
+	kubeAPI, err := k8s.NewAPI(kubeconfigPath, kubeContext, impersonate, impersonateGroup, 0)
+	if err != nil {
+		return false, err
+	}
+
+	_, err = kubeAPI.CoreV1().Namespaces().Get(ctx, controlPlaneNamespace, metav1.GetOptions{})
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			return false, nil
+		}
+		return false, err
+	}
+
+	_, _, err = healthcheck.FetchCurrentConfiguration(ctx, kubeAPI, controlPlaneNamespace)
+	if err != nil {
+		if kerrors.IsNotFound(err) {
+			return false, nil
+		}
+		return false, err
+	}
+
+	return true, nil
+}
diff --git a/jaeger/cmd/root.go b/jaeger/cmd/root.go
new file mode 100644
index 000000000..7da564123
--- /dev/null
+++ b/jaeger/cmd/root.go
@@ -0,0 +1,61 @@
+package cmd
+
+import (
+	"fmt"
+	"regexp"
+
+	log "github.com/sirupsen/logrus"
+	"github.com/spf13/cobra"
+)
+
+const (
+	defaultLinkerdNamespace = "linkerd"
+)
+
+var (
+	apiAddr               string // An empty value means "use the Kubernetes configuration"
+	controlPlaneNamespace string
+	kubeconfigPath        string
+	kubeContext           string
+	impersonate           string
+	impersonateGroup      []string
+	verbose               bool
+
+	// These regexs are not as strict as they could be, but are a quick and dirty
+	// sanity check against illegal characters.
+	alphaNumDash = regexp.MustCompile(`^[a-zA-Z0-9-]+$`)
+)
+
+// NewCmdJaeger returns a new jeager command
+func NewCmdJaeger() *cobra.Command {
+	jaegerCmd := &cobra.Command{
+		Use:   "jaeger",
+		Short: "jaeger manages the jaeger extension of Linkerd service mesh",
+		Long:  `jaeger manages the jaeger extension of Linkerd service mesh.`,
+		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+			// enable / disable logging
+			if verbose {
+				log.SetLevel(log.DebugLevel)
+			} else {
+				log.SetLevel(log.PanicLevel)
+			}
+
+			if !alphaNumDash.MatchString(controlPlaneNamespace) {
+				return fmt.Errorf("%s is not a valid namespace", controlPlaneNamespace)
+			}
+
+			return nil
+		},
+	}
+
+	jaegerCmd.PersistentFlags().StringVarP(&controlPlaneNamespace, "linkerd-namespace", "L", defaultLinkerdNamespace, "Namespace in which Linkerd is installed [$LINKERD_NAMESPACE]")
+	jaegerCmd.PersistentFlags().StringVar(&kubeconfigPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests")
+	jaegerCmd.PersistentFlags().StringVar(&kubeContext, "context", "", "Name of the kubeconfig context to use")
+	jaegerCmd.PersistentFlags().StringVar(&impersonate, "as", "", "Username to impersonate for Kubernetes operations")
+	jaegerCmd.PersistentFlags().StringArrayVar(&impersonateGroup, "as-group", []string{}, "Group to impersonate for Kubernetes operations")
+	jaegerCmd.PersistentFlags().StringVar(&apiAddr, "api-addr", "", "Override kubeconfig and communicate directly with the control plane at host:port (mostly for testing)")
+	jaegerCmd.PersistentFlags().BoolVar(&verbose, "verbose", false, "Turn on debug logging")
+	jaegerCmd.AddCommand(newCmdInstall())
+
+	return jaegerCmd
+}
diff --git a/jaeger/static/generate.go b/jaeger/static/generate.go
new file mode 100644
index 000000000..1e2ab2b14
--- /dev/null
+++ b/jaeger/static/generate.go
@@ -0,0 +1,21 @@
+// +build ignore
+
+package main
+
+import (
+	"github.com/linkerd/linkerd2/jaeger/static"
+	"github.com/shurcooL/vfsgen"
+	log "github.com/sirupsen/logrus"
+)
+
+func main() {
+	err := vfsgen.Generate(static.Templates, vfsgen.Options{
+		Filename:     "generated_jaeger_templates.gogen.go",
+		PackageName:  "static",
+		BuildTags:    "prod",
+		VariableName: "Templates",
+	})
+	if err != nil {
+		log.Fatalln(err)
+	}
+}
diff --git a/jaeger/static/templates.go b/jaeger/static/templates.go
new file mode 100644
index 000000000..6ab902096
--- /dev/null
+++ b/jaeger/static/templates.go
@@ -0,0 +1,31 @@
+//go:generate go run generate.go
+// +build !prod
+
+package static
+
+import (
+	"net/http"
+	"path"
+	"path/filepath"
+	"runtime"
+)
+
+// Templates that will be rendered by `linkerd install`. This is only used on
+// dev builds.
+var Templates http.FileSystem = http.Dir(path.Join(getRepoRoot(), "jaeger/charts"))
+
+// getRepoRoot returns the full path to the root of the repo. We assume this
+// function is only called from the `Templates` var above, and that this source
+// file lives at `pkg/charts/static`, relative to the root of the repo.
+func getRepoRoot() string {
+	// /foo/bar/linkerd2/pkg/charts/static/templates.go
+	_, filename, _, _ := runtime.Caller(0)
+
+	// /foo/bar/linkerd2/pkg/charts/static
+	dir := filepath.Dir(filename)
+
+	// filepath.Dir returns the parent directory, so that combined with joining
+	// ".." walks 3 levels up the tree:
+	// /foo/bar/linkerd2
+	return filepath.Dir(path.Join(dir, ".."))
+}
diff --git a/jaeger/values/values.go b/jaeger/values/values.go
index c0f215527..4f36f5925 100644
--- a/jaeger/values/values.go
+++ b/jaeger/values/values.go
@@ -3,6 +3,7 @@ package values
 import (
 	"fmt"
 
+	"github.com/linkerd/linkerd2/jaeger/static"
 	"github.com/linkerd/linkerd2/pkg/charts"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	"helm.sh/helm/v3/pkg/chart/loader"
@@ -46,7 +47,7 @@ func readDefaults(chartDir string) (*Values, error) {
 		Name: chartutil.ValuesfileName,
 	}
 
-	if err := charts.ReadFile(chartDir, valuesFile); err != nil {
+	if err := charts.ReadFile(static.Templates, chartDir, valuesFile); err != nil {
 		return nil, err
 	}
 
diff --git a/pkg/charts/charts.go b/pkg/charts/charts.go
index 1d6fc40ff..b3c191fa1 100644
--- a/pkg/charts/charts.go
+++ b/pkg/charts/charts.go
@@ -2,6 +2,7 @@ package charts
 
 import (
 	"bytes"
+	"net/http"
 	"path"
 	"strings"
 
@@ -22,14 +23,16 @@ type Chart struct {
 	Namespace string
 	RawValues []byte
 	Files     []*loader.BufferedFile
+	Fs        http.FileSystem
 }
 
 func (c *Chart) render(partialsFiles []*loader.BufferedFile) (bytes.Buffer, error) {
-	if err := FilesReader(c.Dir+"/", c.Files); err != nil {
+	if err := FilesReader(c.Fs, c.Dir+"/", c.Files); err != nil {
 		return bytes.Buffer{}, err
 	}
 
-	if err := FilesReader("", partialsFiles); err != nil {
+	// static.Templates is used as partials are always available there
+	if err := FilesReader(static.Templates, "", partialsFiles); err != nil {
 		return bytes.Buffer{}, err
 	}
 
@@ -115,12 +118,12 @@ func (c *Chart) RenderNoPartials() (bytes.Buffer, error) {
 }
 
 // ReadFile updates the buffered file with the data read from disk
-func ReadFile(dir string, f *loader.BufferedFile) error {
+func ReadFile(fs http.FileSystem, dir string, f *loader.BufferedFile) error {
 	filename := dir + f.Name
 	if dir == "" {
 		filename = filename[7:]
 	}
-	file, err := static.Templates.Open(filename)
+	file, err := fs.Open(filename)
 	if err != nil {
 		return err
 	}
@@ -136,9 +139,9 @@ func ReadFile(dir string, f *loader.BufferedFile) error {
 }
 
 // FilesReader reads all the files from a directory
-func FilesReader(dir string, files []*loader.BufferedFile) error {
+func FilesReader(fs http.FileSystem, dir string, files []*loader.BufferedFile) error {
 	for _, f := range files {
-		if err := ReadFile(dir, f); err != nil {
+		if err := ReadFile(fs, dir, f); err != nil {
 			return err
 		}
 	}
diff --git a/pkg/charts/cni/values.go b/pkg/charts/cni/values.go
index 66144032d..4a1af683e 100644
--- a/pkg/charts/cni/values.go
+++ b/pkg/charts/cni/values.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -56,7 +57,7 @@ func readDefaults(chartDir string) (*Values, error) {
 	file := &loader.BufferedFile{
 		Name: chartutil.ValuesfileName,
 	}
-	if err := charts.ReadFile(chartDir, file); err != nil {
+	if err := charts.ReadFile(static.Templates, chartDir, file); err != nil {
 		return nil, err
 	}
 	values := Values{}
diff --git a/pkg/charts/linkerd2/values.go b/pkg/charts/linkerd2/values.go
index 30e6d1ce7..ac3cbfb0b 100644
--- a/pkg/charts/linkerd2/values.go
+++ b/pkg/charts/linkerd2/values.go
@@ -4,14 +4,14 @@ import (
 	"fmt"
 	"time"
 
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
 	"github.com/imdario/mergo"
 	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"github.com/linkerd/linkerd2/pkg/version"
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"sigs.k8s.io/yaml"
 )
 
@@ -288,7 +288,7 @@ func readDefaults(chartDir string, ha bool) (*Values, error) {
 		})
 	}
 
-	if err := charts.FilesReader(chartDir, valuesFiles); err != nil {
+	if err := charts.FilesReader(static.Templates, chartDir, valuesFiles); err != nil {
 		return nil, err
 	}
 
diff --git a/pkg/charts/multicluster/values.go b/pkg/charts/multicluster/values.go
index 31c625976..c7a25b176 100644
--- a/pkg/charts/multicluster/values.go
+++ b/pkg/charts/multicluster/values.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 
 	"github.com/linkerd/linkerd2/pkg/charts"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"helm.sh/helm/v3/pkg/chart/loader"
 	"helm.sh/helm/v3/pkg/chartutil"
@@ -77,7 +78,7 @@ func readDefaults(chartDir string) (*Values, error) {
 	file := &loader.BufferedFile{
 		Name: chartutil.ValuesfileName,
 	}
-	if err := charts.ReadFile(chartDir, file); err != nil {
+	if err := charts.ReadFile(static.Templates, chartDir, file); err != nil {
 		return nil, err
 	}
 	values := Values{}
diff --git a/pkg/charts/static/generate.go b/pkg/charts/static/generate.go
index 24e645141..8c1299f59 100644
--- a/pkg/charts/static/generate.go
+++ b/pkg/charts/static/generate.go
@@ -10,7 +10,7 @@ import (
 
 func main() {
 	err := vfsgen.Generate(static.Templates, vfsgen.Options{
-		Filename:     "generated_templates.gogen.go",
+		Filename:     "generated_linkerd_templates.gogen.go",
 		PackageName:  "static",
 		BuildTags:    "prod",
 		VariableName: "Templates",
diff --git a/pkg/inject/inject.go b/pkg/inject/inject.go
index 2bea99fe3..85883a747 100644
--- a/pkg/inject/inject.go
+++ b/pkg/inject/inject.go
@@ -18,6 +18,7 @@ import (
 	"github.com/linkerd/linkerd2/pkg/charts"
 	"github.com/linkerd/linkerd2/pkg/charts/linkerd2"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	log "github.com/sirupsen/logrus"
 	"helm.sh/helm/v3/pkg/chart/loader"
@@ -292,6 +293,7 @@ func (conf *ResourceConfig) GetPatch(injectProxy bool) ([]byte, error) {
 		Namespace: conf.values.GetGlobal().Namespace,
 		RawValues: rawValues,
 		Files:     files,
+		Fs:        static.Templates,
 	}
 	buf, err := chart.Render()
 	if err != nil {


Test patch:
diff --git a/cli/cmd/install_helm_test.go b/cli/cmd/install_helm_test.go
index 3945fc73b..be57750db 100644
--- a/cli/cmd/install_helm_test.go
+++ b/cli/cmd/install_helm_test.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/linkerd/linkerd2/pkg/charts"
 	l5dcharts "github.com/linkerd/linkerd2/pkg/charts/linkerd2"
+	"github.com/linkerd/linkerd2/pkg/charts/static"
 	"github.com/linkerd/linkerd2/pkg/k8s"
 	"helm.sh/helm/v3/pkg/chart"
 	"helm.sh/helm/v3/pkg/chart/loader"
@@ -348,7 +349,7 @@ func readValuesFile(t *testing.T, path string) []byte {
 		{Name: chartutil.ValuesfileName},
 	}
 
-	if err := charts.FilesReader(path+"/", valuesFiles); err != nil {
+	if err := charts.FilesReader(static.Templates, path+"/", valuesFiles); err != nil {
 		t.Fatal("Unexpected error", err)
 	}