From a7f6c612c2bd86e977c68a797251ea40bc40f050 Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Wed, 20 Dec 2023 20:45:26 +0000 Subject: [PATCH] separate rendering kots kinds and upstream --- pkg/base/render.go | 17 +++++-- pkg/base/replicated.go | 91 ++++++++++++++--------------------- pkg/base/replicated_test.go | 42 +++++++++------- pkg/base/templates.go | 2 +- pkg/pull/pull.go | 17 +++++-- pkg/rewrite/rewrite.go | 17 +++++-- pkg/tests/base/render_test.go | 39 ++++++++------- 7 files changed, 126 insertions(+), 99 deletions(-) diff --git a/pkg/base/render.go b/pkg/base/render.go index 11ea7c9f9b..b9bcdbb504 100644 --- a/pkg/base/render.go +++ b/pkg/base/render.go @@ -2,6 +2,7 @@ package base import ( "github.com/pkg/errors" + "github.com/replicatedhq/kots/pkg/kotsutil" "github.com/replicatedhq/kots/pkg/logger" registrytypes "github.com/replicatedhq/kots/pkg/registry/types" upstreamtypes "github.com/replicatedhq/kots/pkg/upstream/types" @@ -21,18 +22,28 @@ type RenderOptions struct { Log *logger.CLILogger } +// RenderKotsKinds is responsible for rendering KOTS custom resources +func RenderKotsKinds(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (map[string][]byte, error) { + renderedKotsKinds, err := renderKotsKinds(u, renderOptions) + if err != nil { + return nil, errors.Wrap(err, "failed to render kots kinds") + } + + return renderedKotsKinds, nil +} + // RenderUpstream is responsible for any conversions or transpilation steps are required // to take an upstream and make it a valid kubernetes base -func RenderUpstream(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (base *Base, helmBases []Base, renderedKotsKinds map[string][]byte, err error) { +func RenderUpstream(u *upstreamtypes.Upstream, renderOptions *RenderOptions, renderedKotsKinds *kotsutil.KotsKinds) (base *Base, helmBases []Base, err error) { if u.Type == "helm" { base, err = RenderHelm(u, renderOptions) return } if u.Type == "replicated" { - base, helmBases, renderedKotsKinds, err = renderReplicated(u, renderOptions) + base, helmBases, err = renderReplicated(u, renderOptions, renderedKotsKinds) return } - return nil, nil, nil, errors.New("unknown upstream type") + return nil, nil, errors.New("unknown upstream type") } diff --git a/pkg/base/replicated.go b/pkg/base/replicated.go index 90a6aafd06..c22306b96c 100644 --- a/pkg/base/replicated.go +++ b/pkg/base/replicated.go @@ -23,7 +23,6 @@ import ( kotsv1beta2 "github.com/replicatedhq/kotskinds/apis/kots/v1beta2" kotsscheme "github.com/replicatedhq/kotskinds/client/kotsclientset/scheme" "github.com/replicatedhq/kotskinds/pkg/helmchart" - troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" troubleshootscheme "github.com/replicatedhq/troubleshoot/pkg/client/troubleshootclientset/scheme" velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "helm.sh/helm/v3/pkg/chart" @@ -44,33 +43,15 @@ type Document struct { Kind string `yaml:"kind"` } -func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (*Base, []Base, map[string][]byte, error) { +func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions, renderedKotsKinds *kotsutil.KotsKinds) (*Base, []Base, error) { commonBase := Base{ Files: []BaseFile{}, Bases: []Base{}, } - builder, itemValues, err := NewConfigContextTemplateBuilder(u, renderOptions) - if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to create new config context template builder") - } - - kotsKinds, err := getKotsKinds(u) + builder, _, err := NewConfigContextTemplateBuilder(u, renderOptions) if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to find config file") - } - - versionInfo := template.VersionInfoFromInstallationSpec(renderOptions.Sequence, renderOptions.IsAirgap, kotsKinds.Installation.Spec) - appInfo := template.ApplicationInfo{Slug: renderOptions.AppSlug} - - renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, itemValues, kotsKinds.License, &kotsKinds.KotsApplication, renderOptions.RegistrySettings, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, true) - if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to template config objects") - } - - renderedKotsKinds, err := renderKotsKinds(u.Files, renderedConfig, renderOptions, builder) - if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to render the kots kinds") + return nil, nil, errors.Wrap(err, "failed to create new config context template builder") } for _, upstreamFile := range u.Files { @@ -94,15 +75,15 @@ func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions) ( upstreamFile.Content = bytes.Join(newContent, []byte("\n---\n")) } - c, err := processVariadicConfig(&upstreamFile, renderedConfig, renderOptions.Log) + c, err := processVariadicConfig(&upstreamFile, renderedKotsKinds.Config, renderOptions.Log) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "failed to process variadic config in file %s", upstreamFile.Path) + return nil, nil, errors.Wrapf(err, "failed to process variadic config in file %s", upstreamFile.Path) } upstreamFile.Content = c baseFile, err := upstreamFileToBaseFile(upstreamFile, *builder, renderOptions.Log) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "failed to convert upstream file %s to base", upstreamFile.Path) + return nil, nil, errors.Wrapf(err, "failed to convert upstream file %s to base", upstreamFile.Path) } baseFiles := convertToSingleDocBaseFiles([]BaseFile{baseFile}) @@ -110,7 +91,7 @@ func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions) ( include, err := f.ShouldBeIncludedInBaseKustomization(renderOptions.ExcludeKotsKinds) if err != nil { if _, ok := err.(ParseError); !ok { - return nil, nil, nil, errors.Wrapf(err, "failed to determine if file %s should be included in base", f.Path) + return nil, nil, errors.Wrapf(err, "failed to determine if file %s should be included in base", f.Path) } } if include { @@ -127,21 +108,21 @@ func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions) ( // NOTE: we only render v1beta1 HelmCharts to base kotsV1Beta1HelmCharts, err := findAllKotsV1Beta1HelmCharts(u.Files, *builder, renderOptions.Log) if err != nil { - return nil, nil, nil, errors.Wrap(err, "failed to find helm charts") + return nil, nil, errors.Wrap(err, "failed to find helm charts") } helmBases := []Base{} for _, kotsHelmChart := range kotsV1Beta1HelmCharts { helmBase, err := renderReplicatedHelmChart(&kotsHelmChart, u.Files, renderOptions, builder) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "failed to render helm chart %s", kotsHelmChart.Name) + return nil, nil, errors.Wrapf(err, "failed to render helm chart %s", kotsHelmChart.Name) } else if helmBase == nil { continue } renderedHelmBase, err := renderReplicatedHelmBase(u, renderOptions, *helmBase, *builder) if err != nil { - return nil, nil, nil, errors.Wrapf(err, "failed to render helm chart base %s", helmBase.Path) + return nil, nil, errors.Wrapf(err, "failed to render helm chart base %s", helmBase.Path) } if kotsHelmChart.Spec.UseHelmInstall { @@ -151,13 +132,31 @@ func renderReplicated(u *upstreamtypes.Upstream, renderOptions *RenderOptions) ( } } - return &commonBase, helmBases, renderedKotsKinds, nil + return &commonBase, helmBases, nil } -func renderKotsKinds(upstreamFiles []upstreamtypes.UpstreamFile, renderedConfig *kotsv1beta1.Config, renderOptions *RenderOptions, builder *template.Builder) (map[string][]byte, error) { - renderedKotsKinds := make(map[string][]byte) +func renderKotsKinds(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (map[string][]byte, error) { + renderedKotsKindsMap := make(map[string][]byte) - for _, upstreamFile := range upstreamFiles { + builder, itemValues, err := NewConfigContextTemplateBuilder(u, renderOptions) + if err != nil { + return nil, errors.Wrap(err, "failed to create new config context template builder") + } + + kotsKinds, err := getTemplatingKotsKinds(u) + if err != nil { + return nil, errors.Wrap(err, "failed to find config file") + } + + versionInfo := template.VersionInfoFromInstallationSpec(renderOptions.Sequence, renderOptions.IsAirgap, kotsKinds.Installation.Spec) + appInfo := template.ApplicationInfo{Slug: renderOptions.AppSlug} + + renderedConfig, err := kotsconfig.TemplateConfigObjects(kotsKinds.Config, itemValues, kotsKinds.License, &kotsKinds.KotsApplication, renderOptions.RegistrySettings, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, true) + if err != nil { + return nil, errors.Wrap(err, "failed to template config objects") + } + + for _, upstreamFile := range u.Files { for _, doc := range util.ConvertToSingleDocs(upstreamFile.Content) { gvk := OverlySimpleGVK{} if err := yaml.Unmarshal(doc, &gvk); err != nil { @@ -212,15 +211,15 @@ func renderKotsKinds(upstreamFiles []upstreamtypes.UpstreamFile, renderedConfig doc = []byte(bytes) } - if existing, exists := renderedKotsKinds[upstreamFile.Path]; exists { + if existing, exists := renderedKotsKindsMap[upstreamFile.Path]; exists { doc = bytes.Join([][]byte{existing, doc}, []byte("\n---\n")) } - renderedKotsKinds[upstreamFile.Path] = doc + renderedKotsKindsMap[upstreamFile.Path] = doc } } - return renderedKotsKinds, nil + return renderedKotsKindsMap, nil } func extractHelmBases(b Base) []Base { @@ -440,7 +439,7 @@ func tryGetConfigFromFileContent(content []byte, log *logger.CLILogger) *kotsv1b return nil } -func getKotsKinds(u *upstreamtypes.Upstream) (*kotsutil.KotsKinds, error) { +func getTemplatingKotsKinds(u *upstreamtypes.Upstream) (*kotsutil.KotsKinds, error) { kotsKinds := &kotsutil.KotsKinds{} for _, file := range u.Files { @@ -455,7 +454,7 @@ func getKotsKinds(u *upstreamtypes.Upstream) (*kotsutil.KotsKinds, error) { decoded, gvk, err := decode(doc, nil, nil) if err != nil { if document.APIVersion == "kots.io/v1beta1" && (document.Kind == "Config" || document.Kind == "License") { - errMessage := fmt.Sprintf("Failed to decode %s", file.Path) + errMessage := fmt.Sprintf("Failed to decode %s: %v", file.Path, string(doc)) return nil, errors.Wrap(err, errMessage) } continue @@ -470,26 +469,10 @@ func getKotsKinds(u *upstreamtypes.Upstream) (*kotsutil.KotsKinds, error) { kotsKinds.KotsApplication = *decoded.(*kotsv1beta1.Application) case "kots.io/v1beta1, Kind=License": kotsKinds.License = decoded.(*kotsv1beta1.License) - case "kots.io/v1beta1, Kind=Identity": - kotsKinds.Identity = decoded.(*kotsv1beta1.Identity) case "kots.io/v1beta1, Kind=IdentityConfig": kotsKinds.IdentityConfig = decoded.(*kotsv1beta1.IdentityConfig) case "kots.io/v1beta1, Kind=Installation": kotsKinds.Installation = *decoded.(*kotsv1beta1.Installation) - case "troubleshoot.sh/v1beta2, Kind=Collector": - kotsKinds.Collector = decoded.(*troubleshootv1beta2.Collector) - case "troubleshoot.sh/v1beta2, Kind=Analyzer": - kotsKinds.Analyzer = decoded.(*troubleshootv1beta2.Analyzer) - case "troubleshoot.sh/v1beta2, Kind=SupportBundle": - kotsKinds.SupportBundle = decoded.(*troubleshootv1beta2.SupportBundle) - case "troubleshoot.sh/v1beta2, Kind=Redactor": - kotsKinds.Redactor = decoded.(*troubleshootv1beta2.Redactor) - case "troubleshoot.sh/v1beta2, Kind=Preflight": - kotsKinds.Preflight = decoded.(*troubleshootv1beta2.Preflight) - case "velero.io/v1, Kind=Backup": - kotsKinds.Backup = decoded.(*velerov1.Backup) - case "app.k8s.io/v1beta1, Kind=Application": - kotsKinds.Application = decoded.(*applicationv1beta1.Application) } } } diff --git a/pkg/base/replicated_test.go b/pkg/base/replicated_test.go index b65b42aa55..91664ae14f 100644 --- a/pkg/base/replicated_test.go +++ b/pkg/base/replicated_test.go @@ -862,7 +862,17 @@ status: {} t.Run(test.name, func(t *testing.T) { req := require.New(t) - base, _, kotsKinds, err := renderReplicated(test.upstream, test.renderOptions) + renderedKotsKindsMap, err := renderKotsKinds(test.upstream, test.renderOptions) + req.NoError(err) + + renderedKotsKinds, err := kotsutil.KotsKindsFromMap(renderedKotsKindsMap) + req.NoError(err) + + expectedKotsKinds, err := kotsutil.KotsKindsFromMap(test.expectedKotsKinds) + req.NoError(err) + req.Equal(expectedKotsKinds, renderedKotsKinds) + + base, _, err := renderReplicated(test.upstream, test.renderOptions, renderedKotsKinds) req.NoError(err) decode := scheme.Codecs.UniversalDeserializer().Decode @@ -876,12 +886,6 @@ status: {} expectedMultidoc := multidocobj.(*corev1.ServiceAccount) - expKindsStruct, err := kotsutil.KotsKindsFromMap(test.expectedKotsKinds) - req.NoError(err) - kindsStruct, err := kotsutil.KotsKindsFromMap(kotsKinds) - req.NoError(err) - req.Equal(expKindsStruct, kindsStruct) - var unmarshaledSecrets []*corev1.Secret for _, expectedSecret := range test.expectedSecrets { secobj, _, err := decode(expectedSecret.Content, nil, nil) @@ -1910,21 +1914,25 @@ version: 1.10.1 t.Run(test.name, func(t *testing.T) { req := require.New(t) - base, helmBase, kotsKinds, err := renderReplicated(test.upstream, test.renderOptions) + renderedKotsKindsMap, err := renderKotsKinds(test.upstream, test.renderOptions) + req.NoError(err) + + renderedKotsKinds, err := kotsutil.KotsKindsFromMap(renderedKotsKindsMap) req.NoError(err) - req.ElementsMatch(test.expectedHelm, helmBase) - req.ElementsMatch(test.expectedBase.Files, base.Files) - expKindsStruct, err := kotsutil.KotsKindsFromMap(test.expectedKotsKinds) + expectedKotsKinds, err := kotsutil.KotsKindsFromMap(test.expectedKotsKinds) req.NoError(err) - kindsStruct, err := kotsutil.KotsKindsFromMap(kotsKinds) + req.Equal(expectedKotsKinds, renderedKotsKinds) + + base, helmBase, err := renderReplicated(test.upstream, test.renderOptions, renderedKotsKinds) req.NoError(err) - req.Equal(expKindsStruct, kindsStruct) + req.ElementsMatch(test.expectedHelm, helmBase) + req.ElementsMatch(test.expectedBase.Files, base.Files) }) } } -func Test_getKotsKinds(t *testing.T) { +func Test_getTemplatingKotsKinds(t *testing.T) { type args struct { u *upstreamtypes.Upstream } @@ -2142,13 +2150,13 @@ spec: for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := getKotsKinds(tt.args.u) + got, err := getTemplatingKotsKinds(tt.args.u) if (err != nil) != tt.wantErr { - t.Errorf("getKotsKinds() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("getTemplatingKotsKinds() error = %v, wantErr %v", err, tt.wantErr) return } if !reflect.DeepEqual(got, tt.want) { - t.Errorf("getKotsKinds() = %v, want %v", got, tt.want) + t.Errorf("getTemplatingKotsKinds() = %v, want %v", got, tt.want) } }) } diff --git a/pkg/base/templates.go b/pkg/base/templates.go index c89925b127..d177a07d5c 100644 --- a/pkg/base/templates.go +++ b/pkg/base/templates.go @@ -8,7 +8,7 @@ import ( ) func NewConfigContextTemplateBuilder(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (*template.Builder, map[string]template.ItemValue, error) { - kotsKinds, err := getKotsKinds(u) + kotsKinds, err := getTemplatingKotsKinds(u) if err != nil { return nil, nil, err } diff --git a/pkg/pull/pull.go b/pkg/pull/pull.go index 9165e06453..6644e57a07 100644 --- a/pkg/pull/pull.go +++ b/pkg/pull/pull.go @@ -320,10 +320,10 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) { Sequence: pullOptions.AppSequence, IsAirgap: pullOptions.AirgapRoot != "", } - log.ActionWithSpinner("Creating base") - io.WriteString(pullOptions.ReportWriter, "Creating base\n") + log.ActionWithSpinner("Rendering KOTS custom resources") + io.WriteString(pullOptions.ReportWriter, "Rendering KOTS custom resources\n") - commonBase, helmBases, renderedKotsKindsMap, err := base.RenderUpstream(u, &renderOptions) + renderedKotsKindsMap, err := base.RenderKotsKinds(u, &renderOptions) if err != nil { log.FinishSpinnerWithError() return "", errors.Wrap(err, "failed to render upstream") @@ -331,8 +331,10 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) { renderedKotsKinds, err := kotsutil.KotsKindsFromMap(renderedKotsKindsMap) if err != nil { + log.FinishSpinnerWithError() return "", errors.Wrap(err, "failed to load rendered kotskinds from map") } + log.FinishSpinner() needsConfig, err := kotsadmconfig.NeedsConfiguration(pullOptions.AppSlug, pullOptions.AppSequence, pullOptions.AirgapRoot != "", renderedKotsKinds, registrySettings) if err != nil { @@ -409,6 +411,15 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) { } } + log.ActionWithSpinner("Creating base") + io.WriteString(pullOptions.ReportWriter, "Creating base\n") + + commonBase, helmBases, err := base.RenderUpstream(u, &renderOptions, renderedKotsKinds) + if err != nil { + log.FinishSpinnerWithError() + return "", errors.Wrap(err, "failed to render upstream") + } + errorFiles := []base.BaseFile{} errorFiles = append(errorFiles, base.PrependBaseFilesPath(commonBase.ListErrorFiles(), commonBase.Path)...) for _, helmBase := range helmBases { diff --git a/pkg/rewrite/rewrite.go b/pkg/rewrite/rewrite.go index dcc55096d5..e552f0f846 100644 --- a/pkg/rewrite/rewrite.go +++ b/pkg/rewrite/rewrite.go @@ -123,18 +123,29 @@ func Rewrite(rewriteOptions RewriteOptions) error { Sequence: rewriteOptions.AppSequence, IsAirgap: rewriteOptions.IsAirgap, } - log.ActionWithSpinner("Creating base") - io.WriteString(rewriteOptions.ReportWriter, "Creating base\n") + log.ActionWithSpinner("Rendering KOTS custom resources") + io.WriteString(rewriteOptions.ReportWriter, "Rendering KOTS custom resources\n") - commonBase, helmBases, renderedKotsKindsMap, err := base.RenderUpstream(u, &renderOptions) + renderedKotsKindsMap, err := base.RenderKotsKinds(u, &renderOptions) if err != nil { + log.FinishSpinnerWithError() return errors.Wrap(err, "failed to render upstream") } renderedKotsKinds, err := kotsutil.KotsKindsFromMap(renderedKotsKindsMap) if err != nil { + log.FinishSpinnerWithError() return errors.Wrap(err, "failed to load rendered kotskinds from map") } + log.FinishSpinner() + + log.ActionWithSpinner("Creating base") + io.WriteString(rewriteOptions.ReportWriter, "Creating base\n") + + commonBase, helmBases, err := base.RenderUpstream(u, &renderOptions, renderedKotsKinds) + if err != nil { + return errors.Wrap(err, "failed to render upstream") + } errorFiles := []base.BaseFile{} errorFiles = append(errorFiles, base.PrependBaseFilesPath(commonBase.ListErrorFiles(), commonBase.Path)...) diff --git a/pkg/tests/base/render_test.go b/pkg/tests/base/render_test.go index eaf6eba1c6..4c48bc0b08 100644 --- a/pkg/tests/base/render_test.go +++ b/pkg/tests/base/render_test.go @@ -112,26 +112,9 @@ func TestRenderUpstream(t *testing.T) { template.TestingDisableKurlValues = true defer func() { template.TestingDisableKurlValues = false }() - gotBase, gotHelmBases, gotKotsKindsFiles, err := base.RenderUpstream(&tt.Upstream, &tt.RenderOptions) + gotKotsKindsFiles, err := base.RenderKotsKinds(&tt.Upstream, &tt.RenderOptions) require.NoError(t, err) - if len(tt.WantBase.Files) > 0 { - if !assert.IsEqual(tt.WantBase, gotBase) { - t.Log(diffJSON(gotBase, tt.WantBase)) - t.FailNow() - } - - if !assert.IsEqual(tt.WantBase.Files, gotBase.Files) { - for idx := range tt.WantBase.Files { - if len(gotBase.Files) > idx && gotBase.Files[idx].Path == tt.WantBase.Files[idx].Path { - t.Log("FILE", tt.WantBase.Files[idx].Path) - t.Log(diffString(string(gotBase.Files[idx].Content), string(tt.WantBase.Files[idx].Content))) - } - } - t.FailNow() - } - } - gotKotsKinds, err := kotsutil.KotsKindsFromMap(gotKotsKindsFiles) require.NoError(t, err, "kots kinds from map") @@ -149,6 +132,26 @@ func TestRenderUpstream(t *testing.T) { require.Equal(t, tt.WantKotsKinds, gotKotsKinds) + gotBase, gotHelmBases, err := base.RenderUpstream(&tt.Upstream, &tt.RenderOptions, gotKotsKinds) + require.NoError(t, err) + + if len(tt.WantBase.Files) > 0 { + if !assert.IsEqual(tt.WantBase, gotBase) { + t.Log(diffJSON(gotBase, tt.WantBase)) + t.FailNow() + } + + if !assert.IsEqual(tt.WantBase.Files, gotBase.Files) { + for idx := range tt.WantBase.Files { + if len(gotBase.Files) > idx && gotBase.Files[idx].Path == tt.WantBase.Files[idx].Path { + t.Log("FILE", tt.WantBase.Files[idx].Path) + t.Log(diffString(string(gotBase.Files[idx].Content), string(tt.WantBase.Files[idx].Content))) + } + } + t.FailNow() + } + } + // TODO: Need to test upstream with multiple Helm charts. // HACK: Also right now "no files" in WantHelmBase implies test does not include any charts. if len(tt.WantHelmBase.Files) == 0 {