From d459ac031f2cb9ab3f12dc1223da4612d33cf1e3 Mon Sep 17 00:00:00 2001 From: Salah Aldeen Al Saleh Date: Thu, 21 Dec 2023 15:20:50 +0000 Subject: [PATCH] load non-rendered config --- pkg/base/replicated.go | 29 ++++- pkg/handlers/config.go | 103 ++++++++++------ pkg/kotsutil/kots.go | 113 ++++-------------- pkg/kotsutil/kots_test.go | 68 +++++++++++ .../cases/kotskinds/upstream/kots-app.yaml | 18 ++- .../kotskinds/upstream/userdata/config.yaml | 2 +- .../wantResults/kotsKinds/kots-app.yaml | 18 ++- .../kotsKinds/userdata/config.yaml | 2 +- .../kotsKinds/userdata/installation.yaml | 7 +- .../overlays/midstream/secret.yaml | 4 +- .../kotsadm-replicated-registry-secret.yaml | 2 +- .../this-cluster/my-app-registry-secret.yaml | 2 +- .../wantResults/upstream/kots-app.yaml | 18 ++- .../wantResults/upstream/userdata/config.yaml | 2 +- .../upstream/userdata/installation.yaml | 7 +- 15 files changed, 215 insertions(+), 180 deletions(-) diff --git a/pkg/base/replicated.go b/pkg/base/replicated.go index b8bd7c69fd..33758d27a2 100644 --- a/pkg/base/replicated.go +++ b/pkg/base/replicated.go @@ -143,15 +143,15 @@ func renderKotsKinds(u *upstreamtypes.Upstream, renderOptions *RenderOptions) (m return nil, errors.Wrap(err, "failed to create new config context template builder") } - templatingKotsKinds, err := getTemplatingKotsKinds(u) + tkk, err := getTemplatingKotsKinds(u) if err != nil { return nil, errors.Wrap(err, "failed to find config file") } - versionInfo := template.VersionInfoFromInstallationSpec(renderOptions.Sequence, renderOptions.IsAirgap, templatingKotsKinds.Installation.Spec) + versionInfo := template.VersionInfoFromInstallationSpec(renderOptions.Sequence, renderOptions.IsAirgap, tkk.Installation.Spec) appInfo := template.ApplicationInfo{Slug: renderOptions.AppSlug} - renderedConfig, err := kotsconfig.TemplateConfigObjects(templatingKotsKinds.Config, itemValues, templatingKotsKinds.License, &templatingKotsKinds.KotsApplication, renderOptions.RegistrySettings, &versionInfo, &appInfo, templatingKotsKinds.IdentityConfig, util.PodNamespace, true) + renderedConfig, err := kotsconfig.TemplateConfigObjects(tkk.Config, itemValues, tkk.License, &tkk.KotsApplication, renderOptions.RegistrySettings, &versionInfo, &appInfo, tkk.IdentityConfig, util.PodNamespace, true) if err != nil { return nil, errors.Wrap(err, "failed to template config objects") } @@ -450,12 +450,29 @@ func getTemplatingKotsKinds(u *upstreamtypes.Upstream) (*kotsutil.KotsKinds, err continue } - if !kotsutil.IsTemplatingKotsKind(document.APIVersion, document.Kind) { + decode := scheme.Codecs.UniversalDeserializer().Decode + 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: %v", file.Path, string(doc)) + return nil, errors.Wrap(err, errMessage) + } continue } - if err := kotsKinds.AddKotsKinds(doc); err != nil { - return nil, errors.Wrapf(err, "failed to add kots kinds from %s", file.Path) + switch gvk.String() { + case "kots.io/v1beta1, Kind=Config": + kotsKinds.Config = decoded.(*kotsv1beta1.Config) + case "kots.io/v1beta1, Kind=ConfigValues": + kotsKinds.ConfigValues = decoded.(*kotsv1beta1.ConfigValues) + case "kots.io/v1beta1, Kind=Application": + kotsKinds.KotsApplication = *decoded.(*kotsv1beta1.Application) + case "kots.io/v1beta1, Kind=License": + kotsKinds.License = decoded.(*kotsv1beta1.License) + case "kots.io/v1beta1, Kind=IdentityConfig": + kotsKinds.IdentityConfig = decoded.(*kotsv1beta1.IdentityConfig) + case "kots.io/v1beta1, Kind=Installation": + kotsKinds.Installation = *decoded.(*kotsv1beta1.Installation) } } } diff --git a/pkg/handlers/config.go b/pkg/handlers/config.go index 947cecaf71..9aee7e35c6 100644 --- a/pkg/handlers/config.go +++ b/pkg/handlers/config.go @@ -270,7 +270,8 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { return } - var templatingKotsKinds *kotsutil.KotsKinds + var kotsKinds *kotsutil.KotsKinds + var nonRenderedConfig *kotsv1beta1.Config var appLicense *kotsv1beta1.License var app apptypes.AppType var localRegistry registrytypes.RegistrySettings @@ -294,8 +295,9 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } - templatingKotsKinds = &k - appLicense = templatingKotsKinds.License + kotsKinds = &k + nonRenderedConfig = kotsKinds.Config + appLicense = kotsKinds.License createNewVersion = true } else { licenseID := helm.GetKotsLicenseID(&helmApp.Release) @@ -320,8 +322,9 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { } k.License = licenseData.License - templatingKotsKinds = &k - appLicense = templatingKotsKinds.License + kotsKinds = &k + nonRenderedConfig = kotsKinds.Config + appLicense = kotsKinds.License } } else { foundApp, err := store.GetStore().GetAppFromSlug(appSlug) @@ -341,7 +344,7 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { return } - archiveDir, err := ioutil.TempDir("", "kotsadm") + archiveDir, err := os.MkdirTemp("", "kotsadm") if err != nil { liveAppConfigResponse.Error = "failed to create temp dir" logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) @@ -358,7 +361,7 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { return } - templatingKotsKinds, err = kotsutil.LoadTemplatingKotsKinds(archiveDir) + kotsKinds, err = kotsutil.LoadKotsKinds(archiveDir) if err != nil { liveAppConfigResponse.Error = "failed to load kots kinds from path" logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) @@ -366,6 +369,15 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { return } + // get the non-rendered config from the upstream directory because we have to re-render it with the new values + nonRenderedConfig, err = kotsutil.FindConfigInPath(filepath.Join(archiveDir, "upstream")) + if err != nil { + liveAppConfigResponse.Error = "failed to find non-rendered config" + logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, liveAppConfigResponse) + return + } + registryInfo, err := store.GetStore().GetRegistryDetailsForApp(foundApp.ID) if err != nil { liveAppConfigResponse.Error = "failed to get app registry info" @@ -390,9 +402,9 @@ func (h *Handler) LiveAppConfig(w http.ResponseWriter, r *http.Request) { sequence += 1 } - versionInfo := template.VersionInfoFromInstallationSpec(sequence, app.GetIsAirgap(), templatingKotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) + versionInfo := template.VersionInfoFromInstallationSpec(sequence, app.GetIsAirgap(), kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) appInfo := template.ApplicationInfo{Slug: app.GetSlug()} - renderedConfig, err := kotsconfig.TemplateConfigObjects(templatingKotsKinds.Config, configValues, appLicense, &templatingKotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, templatingKotsKinds.IdentityConfig, app.GetNamespace(), false) + renderedConfig, err := kotsconfig.TemplateConfigObjects(nonRenderedConfig, configValues, appLicense, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, app.GetNamespace(), false) if err != nil { liveAppConfigResponse.Error = "failed to render templates" logger.Error(errors.Wrap(err, liveAppConfigResponse.Error)) @@ -482,7 +494,8 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { return } - var templatingKotsKinds *kotsutil.KotsKinds + var kotsKinds *kotsutil.KotsKinds + var nonRenderedConfig *kotsv1beta1.Config var license *kotsv1beta1.License var localRegistry registrytypes.RegistrySettings var app apptypes.AppType @@ -519,8 +532,9 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } - templatingKotsKinds = &k - license = templatingKotsKinds.License + kotsKinds = &k + nonRenderedConfig = kotsKinds.Config + license = kotsKinds.License createNewVersion = true } else { appKotsKinds, err := helm.GetKotsKindsFromHelmApp(helmApp) @@ -554,8 +568,9 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { } k.License = licenseData.License - templatingKotsKinds = &k - license = templatingKotsKinds.License + kotsKinds = &k + nonRenderedConfig = kotsKinds.Config + license = kotsKinds.License } } else { foundApp, err := store.GetStore().GetAppFromSlug(appSlug) @@ -590,7 +605,7 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { return } - archiveDir, err := ioutil.TempDir("", "kotsadm") + archiveDir, err := os.MkdirTemp("", "kotsadm") if err != nil { currentAppConfigResponse.Error = "failed to create temp dir" logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) @@ -607,7 +622,7 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { return } - templatingKotsKinds, err = kotsutil.LoadTemplatingKotsKinds(archiveDir) + kotsKinds, err = kotsutil.LoadKotsKinds(archiveDir) if err != nil { currentAppConfigResponse.Error = "failed to load kots kinds from path" logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) @@ -615,6 +630,15 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { return } + // get the non-rendered config from the upstream directory because we have to re-render it with the new values + nonRenderedConfig, err = kotsutil.FindConfigInPath(filepath.Join(archiveDir, "upstream")) + if err != nil { + currentAppConfigResponse.Error = "failed to find non-rendered config" + logger.Error(errors.Wrap(err, currentAppConfigResponse.Error)) + JSON(w, http.StatusInternalServerError, currentAppConfigResponse) + return + } + registryInfo, err := store.GetStore().GetRegistryDetailsForApp(foundApp.ID) if err != nil { currentAppConfigResponse.Error = "failed to get app registry info" @@ -639,8 +663,8 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { // get values from saved app version configValues := map[string]template.ItemValue{} - if templatingKotsKinds.ConfigValues != nil { - for key, value := range templatingKotsKinds.ConfigValues.Spec.Values { + if kotsKinds.ConfigValues != nil { + for key, value := range kotsKinds.ConfigValues.Spec.Values { generatedValue := template.ItemValue{ Default: value.Default, Value: value.Value, @@ -655,9 +679,9 @@ func (h *Handler) CurrentAppConfig(w http.ResponseWriter, r *http.Request) { sequence += 1 } - versionInfo := template.VersionInfoFromInstallationSpec(sequence, app.GetIsAirgap(), templatingKotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) + versionInfo := template.VersionInfoFromInstallationSpec(sequence, app.GetIsAirgap(), kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) appInfo := template.ApplicationInfo{Slug: app.GetSlug()} - renderedConfig, err := kotsconfig.TemplateConfigObjects(templatingKotsKinds.Config, configValues, license, &templatingKotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, templatingKotsKinds.IdentityConfig, app.GetNamespace(), false) + renderedConfig, err := kotsconfig.TemplateConfigObjects(nonRenderedConfig, configValues, license, &kotsKinds.KotsApplication, localRegistry, &versionInfo, &appInfo, kotsKinds.IdentityConfig, app.GetNamespace(), false) if err != nil { logger.Error(err) currentAppConfigResponse.Error = "failed to render templates" @@ -749,12 +773,12 @@ func getAppConfigValueForFile(downloadApp *apptypes.App, sequence int64, filenam return "", errors.Wrap(err, "failed to get app version archive") } - templatingKotsKinds, err := kotsutil.LoadTemplatingKotsKinds(archiveDir) + kotsKinds, err := kotsutil.LoadKotsKinds(archiveDir) if err != nil { return "", errors.Wrap(err, "failed to load kots kinds from archive") } - for _, v := range templatingKotsKinds.ConfigValues.Spec.Values { + for _, v := range kotsKinds.ConfigValues.Spec.Values { if v.Filename == filename { return v.Value, nil } @@ -770,7 +794,7 @@ func updateAppConfig(updateApp *apptypes.App, sequence int64, configGroups []kot Success: false, } - archiveDir, err := ioutil.TempDir("", "kotsadm") + archiveDir, err := os.MkdirTemp("", "kotsadm") if err != nil { updateAppConfigResponse.Error = "failed to create temp dir" return updateAppConfigResponse, err @@ -783,7 +807,7 @@ func updateAppConfig(updateApp *apptypes.App, sequence int64, configGroups []kot return updateAppConfigResponse, err } - templatingKotsKinds, err := kotsutil.LoadTemplatingKotsKinds(archiveDir) + kotsKinds, err := kotsutil.LoadKotsKinds(archiveDir) if err != nil { updateAppConfigResponse.Error = "failed to load kots kinds from path" return updateAppConfigResponse, err @@ -800,11 +824,11 @@ func updateAppConfig(updateApp *apptypes.App, sequence int64, configGroups []kot // we don't merge, this is a wholesale replacement of the config values // so we don't need the complex logic in kots, we can just write - if templatingKotsKinds.ConfigValues != nil { - values := templatingKotsKinds.ConfigValues.Spec.Values - templatingKotsKinds.ConfigValues.Spec.Values = updateAppConfigValues(values, configGroups) + if kotsKinds.ConfigValues != nil { + values := kotsKinds.ConfigValues.Spec.Values + kotsKinds.ConfigValues.Spec.Values = updateAppConfigValues(values, configGroups) - configValuesSpec, err := templatingKotsKinds.Marshal("kots.io", "v1beta1", "ConfigValues") + configValuesSpec, err := kotsKinds.Marshal("kots.io", "v1beta1", "ConfigValues") if err != nil { updateAppConfigResponse.Error = "failed to marshal config values spec" return updateAppConfigResponse, err @@ -1079,7 +1103,7 @@ func (h *Handler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { return } - templatingKotsKinds, err := kotsutil.LoadTemplatingKotsKinds(archiveDir) + kotsKinds, err := kotsutil.LoadKotsKinds(archiveDir) if err != nil { setAppConfigValuesResponse.Error = "failed to load kots kinds from path" logger.Error(errors.Wrap(err, setAppConfigValuesResponse.Error)) @@ -1087,7 +1111,16 @@ func (h *Handler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { return } - if templatingKotsKinds.Config == nil { + // get the non-rendered config from the upstream directory because we have to re-render it with the new values + nonRenderedConfig, err := kotsutil.FindConfigInPath(filepath.Join(archiveDir, "upstream")) + if err != nil { + setAppConfigValuesResponse.Error = "failed to find non-rendered config" + logger.Error(errors.Wrap(err, setAppConfigValuesResponse.Error)) + JSON(w, http.StatusInternalServerError, setAppConfigValuesResponse) + return + } + + if nonRenderedConfig == nil { setAppConfigValuesResponse.Error = fmt.Sprintf("app %s does not have a config", foundApp.Slug) logger.Errorf(setAppConfigValuesResponse.Error) JSON(w, http.StatusInternalServerError, setAppConfigValuesResponse) @@ -1095,14 +1128,14 @@ func (h *Handler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { } if setAppConfigValuesRequest.Merge { - if err := templatingKotsKinds.DecryptConfigValues(); err != nil { + if err := kotsKinds.DecryptConfigValues(); err != nil { setAppConfigValuesResponse.Error = "failed to decrypt existing values" logger.Error(errors.Wrap(err, setAppConfigValuesResponse.Error)) JSON(w, http.StatusInternalServerError, setAppConfigValuesResponse) return } - newConfigValues, err = mergeConfigValues(templatingKotsKinds.Config, templatingKotsKinds.ConfigValues, newConfigValues) + newConfigValues, err = mergeConfigValues(nonRenderedConfig, kotsKinds.ConfigValues, newConfigValues) if err != nil { setAppConfigValuesResponse.Error = "failed to create new config" logger.Error(errors.Wrap(err, setAppConfigValuesResponse.Error)) @@ -1111,7 +1144,7 @@ func (h *Handler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { } } - newConfig, err := updateConfigObject(templatingKotsKinds.Config, newConfigValues, setAppConfigValuesRequest.Merge) + newConfig, err := updateConfigObject(nonRenderedConfig, newConfigValues, setAppConfigValuesRequest.Merge) if err != nil { setAppConfigValuesResponse.Error = "failed to create new config object" logger.Error(errors.Wrap(err, setAppConfigValuesResponse.Error)) @@ -1149,9 +1182,9 @@ func (h *Handler) SetAppConfigValues(w http.ResponseWriter, r *http.Request) { return } - versionInfo := template.VersionInfoFromInstallationSpec(nextAppSequence, foundApp.IsAirgap, templatingKotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) + versionInfo := template.VersionInfoFromInstallationSpec(nextAppSequence, foundApp.IsAirgap, kotsKinds.Installation.Spec) // sequence +1 because the sequence will be incremented on save (and we want the preview to be accurate) appInfo := template.ApplicationInfo{Slug: foundApp.Slug} - renderedConfig, err := kotsconfig.TemplateConfigObjects(newConfig, configValueMap, templatingKotsKinds.License, &templatingKotsKinds.KotsApplication, registryInfo, &versionInfo, &appInfo, templatingKotsKinds.IdentityConfig, util.PodNamespace, true) + renderedConfig, err := kotsconfig.TemplateConfigObjects(newConfig, configValueMap, kotsKinds.License, &kotsKinds.KotsApplication, registryInfo, &versionInfo, &appInfo, kotsKinds.IdentityConfig, util.PodNamespace, true) if err != nil { setAppConfigValuesResponse.Error = "failed to render templates" logger.Error(errors.Wrap(err, setAppConfigValuesResponse.Error)) diff --git a/pkg/kotsutil/kots.go b/pkg/kotsutil/kots.go index 084e1a7f4c..4bafc62afa 100644 --- a/pkg/kotsutil/kots.go +++ b/pkg/kotsutil/kots.go @@ -144,28 +144,6 @@ func IsKotsKind(apiVersion string, kind string) bool { return false } -func IsTemplatingKotsKind(apiVersion string, kind string) bool { - if apiVersion == "kots.io/v1beta1" && kind == "Config" { - return true - } - if apiVersion == "kots.io/v1beta1" && kind == "ConfigValues" { - return true - } - if apiVersion == "kots.io/v1beta1" && kind == "Application" { - return true - } - if apiVersion == "kots.io/v1beta1" && kind == "License" { - return true - } - if apiVersion == "kots.io/v1beta1" && kind == "IdentityConfig" { - return true - } - if apiVersion == "kots.io/v1beta1" && kind == "Installation" { - return true - } - return false -} - func (k *KotsKinds) EncryptConfigValues() error { if k.ConfigValues == nil || k.Config == nil { return nil @@ -492,7 +470,7 @@ func (o KotsKinds) Marshal(g string, v string, k string) (string, error) { return "", errors.Errorf("unknown gvk %s/%s, Kind=%s", g, v, k) } -func (k *KotsKinds) AddKotsKinds(content []byte) error { +func (k *KotsKinds) addKotsKinds(content []byte) error { decode := scheme.Codecs.UniversalDeserializer().Decode // kots kinds could be part of a multi-yaml doc @@ -670,7 +648,9 @@ func GetKotsKindsPath(archive string) string { return kotsKindsPath } -// LoadKotsKinds loads kots kinds from an app version archive created by kots +// LoadKotsKinds loads kots kinds from an app version archive created by kots. +// it loads the rendered kots kinds if they exist (should always be the case for app version archives created by newer kots versions). +// otherwise it loads the non-rendered kots kinds (app version archives created by older kots versions). func LoadKotsKinds(archive string) (*KotsKinds, error) { kotsKinds := EmptyKotsKinds() @@ -708,74 +688,7 @@ func LoadKotsKinds(archive string) (*KotsKinds, error) { return nil } - if err := kotsKinds.AddKotsKinds(contents); err != nil { - return errors.Wrapf(err, "failed to add kots kinds from %s", path) - } - - return nil - }) - if err != nil { - return nil, errors.Wrap(err, "failed to walk upstream dir") - } - - return &kotsKinds, nil -} - -// GetTemplatingKotsKindsPath returns the path to load the kots kinds that are necessary for templating/rendering from an app version archive created by kots -func GetTemplatingKotsKindsPath(archive string) string { - if archive == "" { - return "" - } - - templatingKotsKindsPath := archive - if _, err := os.Stat(filepath.Join(archive, "upstream")); err == nil { - // contains the non-rendered kots kinds, this directory should always exist. - templatingKotsKindsPath = filepath.Join(archive, "upstream") - } - - return templatingKotsKindsPath -} - -// LoadTemplatingKotsKinds loads the non-rendered config and the kots kinds that are necessary for templating/rendering from an app version archive created by kots. -// templating kots kinds must have valid schema before they're rendered because they're used to render the rest of the kots kinds and app manifests. -func LoadTemplatingKotsKinds(archive string) (*KotsKinds, error) { - kotsKinds := EmptyKotsKinds() - - fromDir := GetTemplatingKotsKindsPath(archive) - if fromDir == "" { - return &kotsKinds, nil - } - - err := filepath.Walk(fromDir, - func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - - if info.IsDir() { - return nil - } - - contents, err := os.ReadFile(path) - if err != nil { - return errors.Wrapf(err, "failed to read file %s", path) - } - - o := OverlySimpleGVK{} - - if err := yaml.Unmarshal(contents, &o); err != nil { - // can't parse as a simple GVK, most probably not a yaml file. log an error based on extension and continue - if ext := filepath.Ext(path); ext == ".yaml" || ext == ".yml" { - logger.Errorf("Failed to parse yaml file %s: %v", path, err) - } - return nil - } - - if !IsTemplatingKotsKind(o.APIVersion, o.Kind) { - return nil - } - - if err := kotsKinds.AddKotsKinds(contents); err != nil { + if err := kotsKinds.addKotsKinds(contents); err != nil { return errors.Wrapf(err, "failed to add kots kinds from %s", path) } @@ -792,7 +705,7 @@ func KotsKindsFromMap(kotsKindsMap map[string][]byte) (*KotsKinds, error) { kotsKinds := EmptyKotsKinds() for path, content := range kotsKindsMap { - if err := kotsKinds.AddKotsKinds(content); err != nil { + if err := kotsKinds.addKotsKinds(content); err != nil { return nil, errors.Wrapf(err, "failed to parse kots kind %s", path) } } @@ -1072,6 +985,20 @@ func LoadConfigValuesFromFile(configValuesFilePath string) (*kotsv1beta1.ConfigV return obj.(*kotsv1beta1.ConfigValues), nil } +func FindConfigInPath(fromDir string) (*kotsv1beta1.Config, error) { + objects, err := loadRuntimeObjectsFromPath("kots.io/v1beta1", "Config", fromDir) + if err != nil { + return nil, errors.Wrapf(err, "failed to load Config from %s", fromDir) + } + + if len(objects) == 0 { + return nil, nil + } + + // we only support having one config spec + return objects[0].(*kotsv1beta1.Config), nil +} + func LoadConfigFromBytes(data []byte) (*kotsv1beta1.Config, error) { decode := scheme.Codecs.UniversalDeserializer().Decode obj, gvk, err := decode(data, nil, nil) diff --git a/pkg/kotsutil/kots_test.go b/pkg/kotsutil/kots_test.go index e76b5ac1e3..53b8de27fb 100644 --- a/pkg/kotsutil/kots_test.go +++ b/pkg/kotsutil/kots_test.go @@ -216,6 +216,74 @@ var _ = Describe("Kots", func() { }) }) + Describe("FindConfigInPath()", func() { + It("returns nil if no config is found", func() { + dir, err := os.MkdirTemp("", "kotsutil-test") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(dir) + + err = os.WriteFile(filepath.Join(dir, "foo.yaml"), []byte("apiVersion: custom.io/v1beta1\nkind: Foo\nmetadata:\n name: foo"), 0644) + Expect(err).ToNot(HaveOccurred()) + + kotsConfig, err := kotsutil.FindConfigInPath(dir) + Expect(err).ToNot(HaveOccurred()) + Expect(kotsConfig).To(BeNil()) + }) + + It("returns the config if found in the root directory", func() { + dir, err := os.MkdirTemp("", "kotsutil-test") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(dir) + + err = os.WriteFile(filepath.Join(dir, "kots-config.yaml"), []byte("apiVersion: kots.io/v1beta1\nkind: Config\nmetadata:\n name: foo"), 0644) + Expect(err).ToNot(HaveOccurred()) + + kotsConfig, err := kotsutil.FindConfigInPath(dir) + Expect(err).ToNot(HaveOccurred()) + Expect(kotsConfig).ToNot(BeNil()) + Expect(kotsConfig.ObjectMeta.Name).To(Equal("foo")) + }) + + It("returns the config if found in a subdirectory", func() { + dir, err := os.MkdirTemp("", "kotsutil-test") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(dir) + + subDir := filepath.Join(dir, "subdir") + err = os.MkdirAll(subDir, 0755) + Expect(err).ToNot(HaveOccurred()) + + err = os.WriteFile(filepath.Join(subDir, "kots-config.yaml"), []byte("apiVersion: kots.io/v1beta1\nkind: Config\nmetadata:\n name: foo"), 0644) + Expect(err).ToNot(HaveOccurred()) + + kotsConfig, err := kotsutil.FindConfigInPath(dir) + Expect(err).ToNot(HaveOccurred()) + Expect(kotsConfig).ToNot(BeNil()) + Expect(kotsConfig.ObjectMeta.Name).To(Equal("foo")) + }) + + It("returns only one config if multiple are found", func() { + dir, err := os.MkdirTemp("", "kotsutil-test") + Expect(err).ToNot(HaveOccurred()) + defer os.RemoveAll(dir) + + subDir := filepath.Join(dir, "subdir") + err = os.MkdirAll(subDir, 0755) + Expect(err).ToNot(HaveOccurred()) + + err = os.WriteFile(filepath.Join(dir, "kots-config.yaml"), []byte("apiVersion: kots.io/v1beta1\nkind: Config\nmetadata:\n name: foo"), 0644) + Expect(err).ToNot(HaveOccurred()) + + err = os.WriteFile(filepath.Join(subDir, "kots-config.yaml"), []byte("apiVersion: kots.io/v1beta1\nkind: Config\nmetadata:\n name: bar"), 0644) + Expect(err).ToNot(HaveOccurred()) + + kotsConfig, err := kotsutil.FindConfigInPath(dir) + Expect(err).ToNot(HaveOccurred()) + Expect(kotsConfig).ToNot(BeNil()) + Expect(kotsConfig.ObjectMeta.Name).To(Equal("foo")) + }) + }) + Describe("EncryptConfigValues()", func() { It("does not error when the config field is missing", func() { kotsKind := &kotsutil.KotsKinds{ diff --git a/pkg/tests/pull/cases/kotskinds/upstream/kots-app.yaml b/pkg/tests/pull/cases/kotskinds/upstream/kots-app.yaml index cc58d192c8..45e6bb34d5 100644 --- a/pkg/tests/pull/cases/kotskinds/upstream/kots-app.yaml +++ b/pkg/tests/pull/cases/kotskinds/upstream/kots-app.yaml @@ -16,11 +16,11 @@ spec: - repl{{ ConfigOption "kots_app_branding_fonts_1_font_sources_1" }} applicationPorts: - serviceName: repl{{ ConfigOption "kots_app_application_ports_1_service_name" }} - servicePort: repl{{ ConfigOption "kots_app_application_ports_1_service_port" }} - localPort: repl{{ ConfigOption "kots_app_application_ports_1_local_port" }} + servicePort: 80 # not templatable yet + localPort: 80 # not templatable yet applicationUrl: repl{{ ConfigOption "kots_app_application_ports_1_application_url" }} - releaseNotes: repl{{ ConfigOption "kots_app_release_notes" }} - allowRollback: repl{{ ConfigOptionEquals "kots_app_allow_rollback" "1" }} + releaseNotes: "my release notes" # not templatable yet + allowRollback: false # not templatable yet statusInformers: - repl{{ ConfigOption "kots_app_status_informers_1" }} graphs: @@ -32,7 +32,7 @@ spec: legend: repl{{ ConfigOption "kots_app_graphs_1_queries_1_legend" }} - query: repl{{ ConfigOption "kots_app_graphs_1_queries_2_query" }} legend: repl{{ ConfigOption "kots_app_graphs_1_queries_2_legend" }} - durationSeconds: repl{{ ConfigOption "kots_app_graphs_1_duration_seconds" }} + durationSeconds: 3600 # not templatable yet yAxisFormat: repl{{ ConfigOption "kots_app_graphs_1_y_axis_format" }} yAxisTemplate: repl{{ ConfigOption "kots_app_graphs_1_y_axis_template" }} minKotsVersion: repl{{ ConfigOption "kots_app_min_kots_version" }} @@ -43,9 +43,5 @@ spec: - repl{{ ConfigOption "kots_app_additional_images_1" }} additionalNamespaces: - repl{{ ConfigOption "kots_app_additional_namespacse_1" }} - requireMinimalRBACPrivileges: repl{{ ConfigOptionEquals "kots_app_require_minimal_rbac_privileges" "1" }} - supportMinimalRBACPrivileges: repl{{ ConfigOptionEquals "kots_app_support_minimal_rbac_privileges" "1" }} - consoleFeatureFlags: - - repl{{ ConfigOption "kots_app_console_feature_flags_1" }} - replicatedRegistryDomain: repl{{ ConfigOption "kots_app_replicated_registry_domain" }} - proxyRegistryDomain: repl{{ ConfigOption "kots_app_proxy_registry_domain" }} + requireMinimalRBACPrivileges: false # not templatable yet + supportMinimalRBACPrivileges: false # not templatable yet diff --git a/pkg/tests/pull/cases/kotskinds/upstream/userdata/config.yaml b/pkg/tests/pull/cases/kotskinds/upstream/userdata/config.yaml index 7883545720..f3c86aa444 100644 --- a/pkg/tests/pull/cases/kotskinds/upstream/userdata/config.yaml +++ b/pkg/tests/pull/cases/kotskinds/upstream/userdata/config.yaml @@ -2,7 +2,7 @@ apiVersion: kots.io/v1beta1 kind: ConfigValues metadata: creationTimestamp: null - name: replicated-kots-app + name: my-app spec: values: hostname: diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/kots-app.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/kots-app.yaml index 696c1955e3..bb8b2d1ca7 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/kots-app.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/kots-app.yaml @@ -16,11 +16,11 @@ spec: - https://cdn.example.com/my-app-font.woff applicationPorts: - serviceName: my-app - servicePort: 80 - localPort: 80 + servicePort: 80 # not templatable yet + localPort: 80 # not templatable yet applicationUrl: http://localhost:80 - releaseNotes: my release notes - allowRollback: false + releaseNotes: "my release notes" # not templatable yet + allowRollback: false # not templatable yet statusInformers: - deployment/my-app graphs: @@ -32,7 +32,7 @@ spec: legend: Memory - query: sum(kube_pod_container_resource_requests_storage_bytes) legend: Storage - durationSeconds: 3600 + durationSeconds: 3600 # not templatable yet yAxisFormat: bytes yAxisTemplate: y-axis-template minKotsVersion: 1.0.0 @@ -43,9 +43,5 @@ spec: - my-app-image:1.0.0 additionalNamespaces: - my-app-namespace - requireMinimalRBACPrivileges: false - supportMinimalRBACPrivileges: false - consoleFeatureFlags: - - my-app-feature - replicatedRegistryDomain: registry.example.com - proxyRegistryDomain: proxy.example.com + requireMinimalRBACPrivileges: false # not templatable yet + supportMinimalRBACPrivileges: false # not templatable yet diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/config.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/config.yaml index 7883545720..f3c86aa444 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/config.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/config.yaml @@ -2,7 +2,7 @@ apiVersion: kots.io/v1beta1 kind: ConfigValues metadata: creationTimestamp: null - name: replicated-kots-app + name: my-app spec: values: hostname: diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/installation.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/installation.yaml index 0393500865..666967fbd7 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/installation.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/userdata/installation.yaml @@ -2,13 +2,14 @@ apiVersion: kots.io/v1beta1 kind: Installation metadata: creationTimestamp: null - name: replicated-kots-app + name: my-app spec: channelID: 1vusIYZLAVxMG6q760OJmRKj5i5 channelName: My Channel knownImages: - - image: my-app-image:1.0.0 - isPrivate: true - image: my-pod-spec-containers-0-image isPrivate: true + - image: my-app-image:1.0.0 + isPrivate: true + releaseNotes: my release notes status: {} diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/overlays/midstream/secret.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/overlays/midstream/secret.yaml index eed11a051b..4d81bc057f 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/overlays/midstream/secret.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/overlays/midstream/secret.yaml @@ -1,6 +1,6 @@ apiVersion: v1 data: - .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= + .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= kind: Secret metadata: annotations: @@ -15,7 +15,7 @@ type: kubernetes.io/dockerconfigjson --- apiVersion: v1 data: - .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= + .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= kind: Secret metadata: annotations: diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/kotsadm-replicated-registry-secret.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/kotsadm-replicated-registry-secret.yaml index 343b9bad89..6f52289831 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/kotsadm-replicated-registry-secret.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/kotsadm-replicated-registry-secret.yaml @@ -1,6 +1,6 @@ apiVersion: v1 data: - .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= + .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= kind: Secret metadata: annotations: diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/my-app-registry-secret.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/my-app-registry-secret.yaml index 5aa6dee4af..a8b369b451 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/my-app-registry-secret.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/my-app-registry-secret.yaml @@ -1,6 +1,6 @@ apiVersion: v1 data: - .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5leGFtcGxlLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= + .dockerconfigjson: eyJhdXRocyI6eyJwcm94eS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9LCJyZWdpc3RyeS5yZXBsaWNhdGVkLmNvbSI6eyJhdXRoIjoiTVhaMWMwOXZhM2hCVm5BeGRHdFNSM1Y1ZUc1R01qTlFTbU54T2pGMmRYTlBiMnQ0UVZad01YUnJVa2QxZVhodVJqSXpVRXBqY1E9PSJ9fX0= kind: Secret metadata: annotations: diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/kots-app.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/kots-app.yaml index cc58d192c8..45e6bb34d5 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/kots-app.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/kots-app.yaml @@ -16,11 +16,11 @@ spec: - repl{{ ConfigOption "kots_app_branding_fonts_1_font_sources_1" }} applicationPorts: - serviceName: repl{{ ConfigOption "kots_app_application_ports_1_service_name" }} - servicePort: repl{{ ConfigOption "kots_app_application_ports_1_service_port" }} - localPort: repl{{ ConfigOption "kots_app_application_ports_1_local_port" }} + servicePort: 80 # not templatable yet + localPort: 80 # not templatable yet applicationUrl: repl{{ ConfigOption "kots_app_application_ports_1_application_url" }} - releaseNotes: repl{{ ConfigOption "kots_app_release_notes" }} - allowRollback: repl{{ ConfigOptionEquals "kots_app_allow_rollback" "1" }} + releaseNotes: "my release notes" # not templatable yet + allowRollback: false # not templatable yet statusInformers: - repl{{ ConfigOption "kots_app_status_informers_1" }} graphs: @@ -32,7 +32,7 @@ spec: legend: repl{{ ConfigOption "kots_app_graphs_1_queries_1_legend" }} - query: repl{{ ConfigOption "kots_app_graphs_1_queries_2_query" }} legend: repl{{ ConfigOption "kots_app_graphs_1_queries_2_legend" }} - durationSeconds: repl{{ ConfigOption "kots_app_graphs_1_duration_seconds" }} + durationSeconds: 3600 # not templatable yet yAxisFormat: repl{{ ConfigOption "kots_app_graphs_1_y_axis_format" }} yAxisTemplate: repl{{ ConfigOption "kots_app_graphs_1_y_axis_template" }} minKotsVersion: repl{{ ConfigOption "kots_app_min_kots_version" }} @@ -43,9 +43,5 @@ spec: - repl{{ ConfigOption "kots_app_additional_images_1" }} additionalNamespaces: - repl{{ ConfigOption "kots_app_additional_namespacse_1" }} - requireMinimalRBACPrivileges: repl{{ ConfigOptionEquals "kots_app_require_minimal_rbac_privileges" "1" }} - supportMinimalRBACPrivileges: repl{{ ConfigOptionEquals "kots_app_support_minimal_rbac_privileges" "1" }} - consoleFeatureFlags: - - repl{{ ConfigOption "kots_app_console_feature_flags_1" }} - replicatedRegistryDomain: repl{{ ConfigOption "kots_app_replicated_registry_domain" }} - proxyRegistryDomain: repl{{ ConfigOption "kots_app_proxy_registry_domain" }} + requireMinimalRBACPrivileges: false # not templatable yet + supportMinimalRBACPrivileges: false # not templatable yet diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/config.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/config.yaml index 7883545720..f3c86aa444 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/config.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/config.yaml @@ -2,7 +2,7 @@ apiVersion: kots.io/v1beta1 kind: ConfigValues metadata: creationTimestamp: null - name: replicated-kots-app + name: my-app spec: values: hostname: diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/installation.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/installation.yaml index 0393500865..666967fbd7 100644 --- a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/installation.yaml +++ b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/userdata/installation.yaml @@ -2,13 +2,14 @@ apiVersion: kots.io/v1beta1 kind: Installation metadata: creationTimestamp: null - name: replicated-kots-app + name: my-app spec: channelID: 1vusIYZLAVxMG6q760OJmRKj5i5 channelName: My Channel knownImages: - - image: my-app-image:1.0.0 - isPrivate: true - image: my-pod-spec-containers-0-image isPrivate: true + - image: my-app-image:1.0.0 + isPrivate: true + releaseNotes: my release notes status: {}