Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/install/installer/cmd/render.go
2498 views
1
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package cmd
6
7
import (
8
"fmt"
9
"io"
10
"io/ioutil"
11
"os"
12
"path/filepath"
13
14
_ "embed"
15
16
"github.com/gitpod-io/gitpod/common-go/log"
17
"github.com/gitpod-io/gitpod/installer/pkg/common"
18
"github.com/gitpod-io/gitpod/installer/pkg/components"
19
"github.com/gitpod-io/gitpod/installer/pkg/config"
20
configv1 "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
21
"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"
22
"github.com/gitpod-io/gitpod/installer/pkg/postprocess"
23
"github.com/spf13/cobra"
24
"sigs.k8s.io/yaml"
25
)
26
27
var renderOpts struct {
28
ConfigFN string
29
Namespace string
30
ValidateConfigDisabled bool
31
UseExperimentalConfig bool
32
FilesDir string
33
}
34
35
// renderCmd represents the render command
36
var renderCmd = &cobra.Command{
37
Use: "render",
38
Short: "Renders the Kubernetes manifests required to install Gitpod",
39
Long: `Renders the Kubernetes manifests required to install Gitpod
40
41
A config file is required which can be generated with the init command.`,
42
Example: ` # Default install.
43
gitpod-installer render --config config.yaml | kubectl apply -f -
44
45
# Install Gitpod into a non-default namespace.
46
gitpod-installer render --config config.yaml --namespace gitpod | kubectl apply -f -`,
47
RunE: func(cmd *cobra.Command, args []string) error {
48
yaml, err := renderFn()
49
if err != nil {
50
return err
51
}
52
53
if renderOpts.FilesDir != "" {
54
err := saveYamlToFiles(renderOpts.FilesDir, yaml)
55
if err != nil {
56
return err
57
}
58
return nil
59
}
60
61
for _, item := range yaml {
62
fmt.Println(item)
63
}
64
65
return nil
66
},
67
}
68
69
func renderFn() ([]string, error) {
70
_, cfgVersion, cfg, err := loadConfig(renderOpts.ConfigFN)
71
if err != nil {
72
return nil, err
73
}
74
75
if cfg.Experimental != nil {
76
if renderOpts.UseExperimentalConfig {
77
fmt.Fprintf(os.Stderr, "rendering using experimental config\n")
78
} else {
79
fmt.Fprintf(os.Stderr, "ignoring experimental config. Use `--use-experimental-config` to include the experimental section in config\n")
80
cfg.Experimental = nil
81
}
82
}
83
84
return renderKubernetesObjects(cfgVersion, cfg)
85
}
86
87
func saveYamlToFiles(dir string, yaml []string) error {
88
for i, mf := range yaml {
89
objs, err := common.YamlToRuntimeObject([]string{mf})
90
if err != nil {
91
return err
92
}
93
obj := objs[0]
94
fn := filepath.Join(dir, fmt.Sprintf("%03d_%s_%s.yaml", i, obj.Kind, obj.Metadata.Name))
95
err = ioutil.WriteFile(fn, []byte(mf), 0644)
96
if err != nil {
97
return err
98
}
99
}
100
return nil
101
}
102
103
func loadConfig(cfgFN string) (rawCfg interface{}, cfgVersion string, cfg *configv1.Config, err error) {
104
var overrideConfig string
105
// Update overrideConfig if cfgFN is not empty
106
switch cfgFN {
107
case "-":
108
b, err := io.ReadAll(os.Stdin)
109
if err != nil {
110
return nil, "", nil, err
111
}
112
overrideConfig = string(b)
113
case "":
114
return nil, "", nil, fmt.Errorf("missing config file")
115
default:
116
cfgBytes, err := ioutil.ReadFile(cfgFN)
117
if err != nil {
118
panic(fmt.Sprintf("couldn't read file %s, %s", cfgFN, err))
119
120
}
121
overrideConfig = string(cfgBytes)
122
}
123
124
rawCfg, cfgVersion, err = config.Load(overrideConfig, rootOpts.StrictConfigParse)
125
if err != nil {
126
err = fmt.Errorf("error loading config: %w", err)
127
return
128
}
129
if cfgVersion != config.CurrentVersion {
130
err = fmt.Errorf("config version is mismatch: expected %s, got %s", config.CurrentVersion, cfgVersion)
131
return
132
}
133
cfg = rawCfg.(*configv1.Config)
134
135
return rawCfg, cfgVersion, cfg, err
136
}
137
138
func renderKubernetesObjects(cfgVersion string, cfg *configv1.Config) ([]string, error) {
139
versionMF, err := getVersionManifest()
140
if err != nil {
141
return nil, err
142
}
143
144
if !renderOpts.ValidateConfigDisabled {
145
apiVersion, err := config.LoadConfigVersion(cfgVersion)
146
if err != nil {
147
return nil, err
148
}
149
res, err := config.Validate(apiVersion, cfg)
150
if err != nil {
151
return nil, err
152
}
153
154
if !res.Valid {
155
res.Marshal(os.Stderr)
156
fmt.Fprintln(os.Stderr, "configuration is invalid")
157
os.Exit(1)
158
}
159
160
// Warnings are printed to stderr
161
for _, r := range res.Warnings {
162
fmt.Fprintf(os.Stderr, "%s\n", r)
163
}
164
}
165
166
ctx, err := common.NewRenderContext(*cfg, *versionMF, renderOpts.Namespace)
167
if err != nil {
168
return nil, err
169
}
170
171
var renderable common.RenderFunc
172
var helmCharts common.HelmFunc
173
switch cfg.Kind {
174
case configv1.InstallationFull:
175
renderable = components.FullObjects
176
helmCharts = components.FullHelmDependencies
177
case configv1.InstallationMeta:
178
renderable = components.MetaObjects
179
helmCharts = components.MetaHelmDependencies
180
case configv1.InstallationIDE:
181
renderable = components.IDEObjects
182
helmCharts = components.IDEHelmDependencies
183
case configv1.InstallationWebApp:
184
renderable = components.WebAppObjects
185
helmCharts = components.WebAppHelmDependencies
186
case configv1.InstallationWorkspace:
187
renderable = components.WorkspaceObjects
188
helmCharts = components.WorkspaceHelmDependencies
189
default:
190
return nil, fmt.Errorf("unsupported installation kind: %s", cfg.Kind)
191
}
192
193
objs, err := common.CompositeRenderFunc(components.CommonObjects, renderable)(ctx)
194
if err != nil {
195
return nil, err
196
}
197
198
k8s := make([]string, 0)
199
for _, o := range objs {
200
fc, err := yaml.Marshal(o)
201
if err != nil {
202
return nil, err
203
}
204
205
k8s = append(k8s, fmt.Sprintf("---\n%s\n", string(fc)))
206
}
207
208
charts, err := common.CompositeHelmFunc(components.CommonHelmDependencies, helmCharts)(ctx)
209
if err != nil {
210
return nil, err
211
}
212
k8s = append(k8s, charts...)
213
214
// convert everything to individual objects
215
runtimeObjs, err := common.YamlToRuntimeObject(k8s)
216
if err != nil {
217
return nil, err
218
}
219
220
// generate a config map with every component installed
221
runtimeObjsAndConfig, err := common.GenerateInstallationConfigMap(ctx, runtimeObjs)
222
if err != nil {
223
return nil, err
224
}
225
226
// sort the objects and return the plain YAML
227
sortedObjs, err := common.DependencySortingRenderFunc(runtimeObjsAndConfig)
228
if err != nil {
229
return nil, err
230
}
231
232
postProcessed, err := postprocess.Run(sortedObjs)
233
if err != nil {
234
return nil, err
235
}
236
237
if err := ctx.WithExperimental(func(ucfg *experimental.Config) error {
238
postProcessed, err = postprocess.Override(ucfg.Overrides, postProcessed)
239
if err != nil {
240
return err
241
}
242
return nil
243
}); err != nil {
244
return nil, err
245
}
246
247
// output the YAML to stdout
248
output := make([]string, 0)
249
for _, c := range postProcessed {
250
output = append(output, fmt.Sprintf("---\n# %s/%s %s\n%s", c.TypeMeta.APIVersion, c.TypeMeta.Kind, c.Metadata.Name, c.Content))
251
}
252
253
return output, nil
254
}
255
256
func init() {
257
rootCmd.AddCommand(renderCmd)
258
259
dir, err := os.Getwd()
260
if err != nil {
261
log.WithError(err).Fatal("Failed to get working directory")
262
}
263
264
renderCmd.PersistentFlags().StringVarP(&renderOpts.ConfigFN, "config", "c", getEnvvar("GITPOD_INSTALLER_CONFIG", filepath.Join(dir, "gitpod.config.yaml")), "path to the config file, use - for stdin")
265
renderCmd.PersistentFlags().StringVarP(&renderOpts.Namespace, "namespace", "n", getEnvvar("NAMESPACE", "default"), "namespace to deploy to")
266
renderCmd.Flags().BoolVar(&renderOpts.ValidateConfigDisabled, "no-validation", false, "if set, the config will not be validated before running")
267
renderCmd.Flags().BoolVar(&renderOpts.UseExperimentalConfig, "use-experimental-config", false, "enable the use of experimental config that is prone to be changed")
268
renderCmd.Flags().StringVar(&renderOpts.FilesDir, "output-split-files", "", "path to output individual Kubernetes manifests to")
269
}
270
271