Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/install/installer/pkg/common/render.go
2500 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 common
6
7
import (
8
"fmt"
9
"strings"
10
11
"github.com/distribution/reference"
12
"helm.sh/helm/v3/pkg/cli/values"
13
"k8s.io/apimachinery/pkg/runtime"
14
"k8s.io/utils/pointer"
15
16
config "github.com/gitpod-io/gitpod/installer/pkg/config/v1"
17
"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"
18
"github.com/gitpod-io/gitpod/installer/pkg/config/versions"
19
)
20
21
// Renderable turns the config into a set of Kubernetes runtime objects
22
type RenderFunc func(cfg *RenderContext) ([]runtime.Object, error)
23
24
type HelmFunc func(cfg *RenderContext) ([]string, error)
25
26
type HelmConfig struct {
27
Enabled bool
28
Values *values.Options
29
}
30
31
func CompositeRenderFunc(f ...RenderFunc) RenderFunc {
32
return func(ctx *RenderContext) ([]runtime.Object, error) {
33
var res []runtime.Object
34
for _, g := range f {
35
obj, err := g(ctx)
36
if err != nil {
37
return nil, err
38
}
39
if len(obj) == 0 {
40
// the RenderFunc chose not to render anything, possibly based on config it received
41
continue
42
}
43
res = append(res, obj...)
44
}
45
return res, nil
46
}
47
}
48
49
func CompositeHelmFunc(f ...HelmFunc) HelmFunc {
50
return func(ctx *RenderContext) ([]string, error) {
51
var res []string
52
for _, g := range f {
53
str, err := g(ctx)
54
if err != nil {
55
return nil, err
56
}
57
res = append(res, str...)
58
}
59
return res, nil
60
}
61
}
62
63
type GeneratedValues struct {
64
StorageAccessKey string
65
StorageSecretKey string
66
InternalRegistryUsername string
67
InternalRegistryPassword string
68
InternalRegistrySharedSecret string
69
}
70
71
type RenderContext struct {
72
VersionManifest versions.Manifest
73
Config config.Config
74
Namespace string
75
Values GeneratedValues
76
77
experimentalConfig *experimental.Config
78
}
79
80
// WithExperimental provides access to the unsupported config. This will only do something
81
// if the unsupported config is present.
82
//
83
// This is intentionally a function rather than an exported field to keep unsupported
84
// config clearly marked as such, and to make sure we can easily remove/disable it.
85
func (r *RenderContext) WithExperimental(mod func(ucfg *experimental.Config) error) error {
86
if r.experimentalConfig == nil {
87
return nil
88
}
89
90
return mod(r.experimentalConfig)
91
}
92
93
func (r *RenderContext) RepoName(repo, name string) string {
94
var ref string
95
if repo == "" {
96
ref = name
97
} else {
98
ref = fmt.Sprintf("%s/%s", strings.TrimSuffix(repo, "/"), name)
99
}
100
pref, err := reference.ParseNormalizedNamed(ref)
101
if err != nil {
102
panic(fmt.Sprintf("cannot parse image repo %s: %v", ref, err))
103
}
104
105
if pointer.BoolDeref(r.Config.DropImageRepo, false) {
106
segs := strings.Split(reference.Path(pref), "/")
107
return fmt.Sprintf("%s/%s", r.Config.Repository, segs[len(segs)-1])
108
}
109
110
return pref.String()
111
}
112
113
func (r *RenderContext) ImageName(repo, name, tag string) string {
114
ref := fmt.Sprintf("%s:%s", r.RepoName(repo, name), tag)
115
pref, err := reference.ParseNamed(ref)
116
if err != nil {
117
panic(fmt.Sprintf("cannot parse image ref %s: %v", ref, err))
118
}
119
if _, ok := pref.(reference.Tagged); !ok {
120
panic(fmt.Sprintf("image ref %s has no tag: %v", ref, err))
121
}
122
123
return ref
124
}
125
126
func (r *RenderContext) ImageDigest(repo, name, digest string) string {
127
ref := fmt.Sprintf("%s@%s", r.RepoName(repo, name), digest)
128
pref, err := reference.ParseNamed(ref)
129
if err != nil {
130
panic(fmt.Sprintf("cannot parse image ref %s: %v", ref, err))
131
}
132
if _, ok := pref.(reference.Digested); !ok {
133
panic(fmt.Sprintf("image ref %s has no digest: %v", ref, err))
134
}
135
return ref
136
}
137
138
// generateValues generates the random values used throughout the context
139
// todo(sje): find a way of persisting these values for updates
140
func (r *RenderContext) generateValues() error {
141
storageAccessKey, err := RandomString(20)
142
if err != nil {
143
return err
144
}
145
r.Values.StorageAccessKey = storageAccessKey
146
147
storageSecretKey, err := RandomString(20)
148
if err != nil {
149
return err
150
}
151
r.Values.StorageSecretKey = storageSecretKey
152
153
internalRegistryUsername, err := RandomString(20)
154
if err != nil {
155
return err
156
}
157
r.Values.InternalRegistryUsername = internalRegistryUsername
158
159
internalRegistryPassword, err := RandomString(20)
160
if err != nil {
161
return err
162
}
163
r.Values.InternalRegistryPassword = internalRegistryPassword
164
165
internalRegistrySharedSecret, err := RandomString(20)
166
if err != nil {
167
return err
168
}
169
r.Values.InternalRegistrySharedSecret = internalRegistrySharedSecret
170
171
return nil
172
}
173
174
// NewRenderContext constructor function to create a new RenderContext with the values generated
175
func NewRenderContext(cfg config.Config, versionManifest versions.Manifest, namespace string) (*RenderContext, error) {
176
us := cfg.Experimental
177
cfg.Experimental = nil
178
179
ctx := &RenderContext{
180
Config: cfg,
181
VersionManifest: versionManifest,
182
Namespace: namespace,
183
experimentalConfig: us,
184
}
185
186
err := ctx.generateValues()
187
if err != nil {
188
return nil, err
189
}
190
191
return ctx, nil
192
}
193
194