Path: blob/main/install/installer/pkg/common/render.go
2500 views
// Copyright (c) 2021 Gitpod GmbH. All rights reserved.1// Licensed under the GNU Affero General Public License (AGPL).2// See License.AGPL.txt in the project root for license information.34package common56import (7"fmt"8"strings"910"github.com/distribution/reference"11"helm.sh/helm/v3/pkg/cli/values"12"k8s.io/apimachinery/pkg/runtime"13"k8s.io/utils/pointer"1415config "github.com/gitpod-io/gitpod/installer/pkg/config/v1"16"github.com/gitpod-io/gitpod/installer/pkg/config/v1/experimental"17"github.com/gitpod-io/gitpod/installer/pkg/config/versions"18)1920// Renderable turns the config into a set of Kubernetes runtime objects21type RenderFunc func(cfg *RenderContext) ([]runtime.Object, error)2223type HelmFunc func(cfg *RenderContext) ([]string, error)2425type HelmConfig struct {26Enabled bool27Values *values.Options28}2930func CompositeRenderFunc(f ...RenderFunc) RenderFunc {31return func(ctx *RenderContext) ([]runtime.Object, error) {32var res []runtime.Object33for _, g := range f {34obj, err := g(ctx)35if err != nil {36return nil, err37}38if len(obj) == 0 {39// the RenderFunc chose not to render anything, possibly based on config it received40continue41}42res = append(res, obj...)43}44return res, nil45}46}4748func CompositeHelmFunc(f ...HelmFunc) HelmFunc {49return func(ctx *RenderContext) ([]string, error) {50var res []string51for _, g := range f {52str, err := g(ctx)53if err != nil {54return nil, err55}56res = append(res, str...)57}58return res, nil59}60}6162type GeneratedValues struct {63StorageAccessKey string64StorageSecretKey string65InternalRegistryUsername string66InternalRegistryPassword string67InternalRegistrySharedSecret string68}6970type RenderContext struct {71VersionManifest versions.Manifest72Config config.Config73Namespace string74Values GeneratedValues7576experimentalConfig *experimental.Config77}7879// WithExperimental provides access to the unsupported config. This will only do something80// if the unsupported config is present.81//82// This is intentionally a function rather than an exported field to keep unsupported83// config clearly marked as such, and to make sure we can easily remove/disable it.84func (r *RenderContext) WithExperimental(mod func(ucfg *experimental.Config) error) error {85if r.experimentalConfig == nil {86return nil87}8889return mod(r.experimentalConfig)90}9192func (r *RenderContext) RepoName(repo, name string) string {93var ref string94if repo == "" {95ref = name96} else {97ref = fmt.Sprintf("%s/%s", strings.TrimSuffix(repo, "/"), name)98}99pref, err := reference.ParseNormalizedNamed(ref)100if err != nil {101panic(fmt.Sprintf("cannot parse image repo %s: %v", ref, err))102}103104if pointer.BoolDeref(r.Config.DropImageRepo, false) {105segs := strings.Split(reference.Path(pref), "/")106return fmt.Sprintf("%s/%s", r.Config.Repository, segs[len(segs)-1])107}108109return pref.String()110}111112func (r *RenderContext) ImageName(repo, name, tag string) string {113ref := fmt.Sprintf("%s:%s", r.RepoName(repo, name), tag)114pref, err := reference.ParseNamed(ref)115if err != nil {116panic(fmt.Sprintf("cannot parse image ref %s: %v", ref, err))117}118if _, ok := pref.(reference.Tagged); !ok {119panic(fmt.Sprintf("image ref %s has no tag: %v", ref, err))120}121122return ref123}124125func (r *RenderContext) ImageDigest(repo, name, digest string) string {126ref := fmt.Sprintf("%s@%s", r.RepoName(repo, name), digest)127pref, err := reference.ParseNamed(ref)128if err != nil {129panic(fmt.Sprintf("cannot parse image ref %s: %v", ref, err))130}131if _, ok := pref.(reference.Digested); !ok {132panic(fmt.Sprintf("image ref %s has no digest: %v", ref, err))133}134return ref135}136137// generateValues generates the random values used throughout the context138// todo(sje): find a way of persisting these values for updates139func (r *RenderContext) generateValues() error {140storageAccessKey, err := RandomString(20)141if err != nil {142return err143}144r.Values.StorageAccessKey = storageAccessKey145146storageSecretKey, err := RandomString(20)147if err != nil {148return err149}150r.Values.StorageSecretKey = storageSecretKey151152internalRegistryUsername, err := RandomString(20)153if err != nil {154return err155}156r.Values.InternalRegistryUsername = internalRegistryUsername157158internalRegistryPassword, err := RandomString(20)159if err != nil {160return err161}162r.Values.InternalRegistryPassword = internalRegistryPassword163164internalRegistrySharedSecret, err := RandomString(20)165if err != nil {166return err167}168r.Values.InternalRegistrySharedSecret = internalRegistrySharedSecret169170return nil171}172173// NewRenderContext constructor function to create a new RenderContext with the values generated174func NewRenderContext(cfg config.Config, versionManifest versions.Manifest, namespace string) (*RenderContext, error) {175us := cfg.Experimental176cfg.Experimental = nil177178ctx := &RenderContext{179Config: cfg,180VersionManifest: versionManifest,181Namespace: namespace,182experimentalConfig: us,183}184185err := ctx.generateValues()186if err != nil {187return nil, err188}189190return ctx, nil191}192193194