1 package cmd
2
3 import (
4 "bytes"
5 "io"
6 "os"
7 "path"
8 "time"
9
10 "github.com/linkerd/linkerd2/pkg/charts"
11 partials "github.com/linkerd/linkerd2/pkg/charts/static"
12 "github.com/linkerd/linkerd2/pkg/flags"
13 "github.com/linkerd/linkerd2/pkg/healthcheck"
14 "github.com/linkerd/linkerd2/viz/static"
15 "github.com/spf13/cobra"
16 "helm.sh/helm/v3/pkg/chart/loader"
17 "helm.sh/helm/v3/pkg/chartutil"
18 "helm.sh/helm/v3/pkg/cli/values"
19 "helm.sh/helm/v3/pkg/engine"
20 )
21
22 var (
23
24 templatesViz = []string{
25 "templates/namespace.yaml",
26 "templates/metrics-api-rbac.yaml",
27 "templates/prometheus-rbac.yaml",
28 "templates/tap-rbac.yaml",
29 "templates/web-rbac.yaml",
30 "templates/psp.yaml",
31 "templates/metrics-api.yaml",
32 "templates/metrics-api-policy.yaml",
33 "templates/admin-policy.yaml",
34 "templates/prometheus.yaml",
35 "templates/prometheus-policy.yaml",
36 "templates/tap.yaml",
37 "templates/tap-policy.yaml",
38 "templates/tap-injector-rbac.yaml",
39 "templates/tap-injector.yaml",
40 "templates/tap-injector-policy.yaml",
41 "templates/web.yaml",
42 "templates/service-profiles.yaml",
43 }
44 )
45
46 func newCmdInstall() *cobra.Command {
47 var skipChecks bool
48 var ignoreCluster bool
49 var ha bool
50 var cniEnabled bool
51 var wait time.Duration
52 var options values.Options
53
54 cmd := &cobra.Command{
55 Use: "install [flags]",
56 Args: cobra.NoArgs,
57 Short: "Output Kubernetes resources to install linkerd-viz extension",
58 Long: `Output Kubernetes resources to install linkerd-viz extension.`,
59 Example: ` # Default install.
60 linkerd viz install | kubectl apply -f -
61
62 The installation can be configured by using the --set, --values, --set-string and --set-file flags.
63 A full list of configurable values can be found at https://www.github.com/linkerd/linkerd2/tree/main/viz/charts/linkerd-viz/README.md
64 `,
65 RunE: func(_ *cobra.Command, _ []string) error {
66 if !skipChecks && !ignoreCluster {
67
68 hc := healthcheck.NewWithCoreChecks(&healthcheck.Options{
69 ControlPlaneNamespace: controlPlaneNamespace,
70 KubeConfig: kubeconfigPath,
71 KubeContext: kubeContext,
72 Impersonate: impersonate,
73 ImpersonateGroup: impersonateGroup,
74 APIAddr: apiAddr,
75 RetryDeadline: time.Now().Add(wait),
76 })
77 hc.RunWithExitOnError()
78 cniEnabled = hc.CNIEnabled
79 }
80 return install(os.Stdout, options, ha, cniEnabled)
81 },
82 }
83
84 cmd.Flags().BoolVar(&skipChecks, "skip-checks", false, `Skip checks for linkerd core control-plane existence`)
85 cmd.Flags().BoolVar(&ignoreCluster, "ignore-cluster", false,
86 "Ignore the current Kubernetes cluster when checking for existing cluster configuration (default false)")
87 cmd.Flags().BoolVar(&ha, "ha", false, `Install Viz Extension in High Availability mode.`)
88 cmd.Flags().DurationVar(&wait, "wait", 300*time.Second, "Wait for core control-plane components to be available")
89
90 flags.AddValueOptionsFlags(cmd.Flags(), &options)
91
92 return cmd
93 }
94
95 func install(w io.Writer, options values.Options, ha, cniEnabled bool) error {
96
97
98 valuesOverrides, err := options.MergeValues(nil)
99 if err != nil {
100 return err
101 }
102
103
104 if controlPlaneNamespace != defaultLinkerdNamespace {
105 valuesOverrides["linkerdNamespace"] = controlPlaneNamespace
106 }
107 if reg := os.Getenv(flags.EnvOverrideDockerRegistry); reg != "" {
108 valuesOverrides["defaultRegistry"] = reg
109 }
110
111 if ha {
112 valuesOverrides, err = charts.OverrideFromFile(valuesOverrides, static.Templates, vizChartName, "values-ha.yaml")
113 if err != nil {
114 return err
115 }
116 }
117
118 if cniEnabled {
119 valuesOverrides["cniEnabled"] = true
120 }
121
122
123
124 return render(w, valuesOverrides)
125 }
126
127 func render(w io.Writer, valuesOverrides map[string]interface{}) error {
128
129 files := []*loader.BufferedFile{
130 {Name: chartutil.ChartfileName},
131 {Name: chartutil.ValuesfileName},
132 }
133
134 for _, template := range templatesViz {
135 files = append(files,
136 &loader.BufferedFile{Name: template},
137 )
138 }
139
140 var partialFiles []*loader.BufferedFile
141 for _, template := range charts.L5dPartials {
142 partialFiles = append(partialFiles,
143 &loader.BufferedFile{Name: template},
144 )
145 }
146
147
148 if err := charts.FilesReader(static.Templates, vizChartName+"/", files); err != nil {
149 return err
150 }
151
152
153 if err := charts.FilesReader(partials.Templates, "", partialFiles); err != nil {
154 return err
155 }
156
157
158 chart, err := loader.LoadFiles(append(files, partialFiles...))
159 if err != nil {
160 return err
161 }
162
163 vals, err := chartutil.CoalesceValues(chart, valuesOverrides)
164 if err != nil {
165 return err
166 }
167
168 vals, err = charts.InsertVersionValues(vals)
169 if err != nil {
170 return err
171 }
172
173 fullValues := map[string]interface{}{
174 "Values": vals,
175 "Release": map[string]interface{}{
176 "Namespace": defaultNamespace,
177 "Service": "CLI",
178 },
179 }
180
181
182 renderedTemplates, err := engine.Render(chart, fullValues)
183 if err != nil {
184 return err
185 }
186
187
188 var buf bytes.Buffer
189 for _, tmpl := range chart.Templates {
190 t := path.Join(chart.Metadata.Name, tmpl.Name)
191 if _, err := buf.WriteString(renderedTemplates[t]); err != nil {
192 return err
193 }
194 }
195
196 _, err = w.Write(buf.Bytes())
197 return err
198 }
199
View as plain text