diff --git a/pkg/kotsutil/kots.go b/pkg/kotsutil/kots.go index 8e939dd79e..a559eebdbd 100644 --- a/pkg/kotsutil/kots.go +++ b/pkg/kotsutil/kots.go @@ -1475,14 +1475,22 @@ func FilterV1Beta1ChartsWithV1Beta2Charts(v1Beta1Charts []kotsv1beta1.HelmChart, } func MustMarshalInstallation(installation *kotsv1beta1.Installation) []byte { + b, err := MarshalRuntimeObject(installation) + if err != nil { + panic(err) + } + return b +} + +func MarshalRuntimeObject(obj runtime.Object) ([]byte, error) { s := json.NewYAMLSerializer(json.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) var b bytes.Buffer - if err := s.Encode(installation, &b); err != nil { - panic(err) + if err := s.Encode(obj, &b); err != nil { + return nil, errors.Wrap(err, "failed to encode object") } - return b.Bytes() + return b.Bytes(), nil } // this is here to avoid a circular dependency diff --git a/pkg/pull/pull.go b/pkg/pull/pull.go index 26679f2add..8efa3df09a 100644 --- a/pkg/pull/pull.go +++ b/pkg/pull/pull.go @@ -33,6 +33,8 @@ import ( "github.com/replicatedhq/kots/pkg/util" kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" kotsv1beta2 "github.com/replicatedhq/kotskinds/apis/kots/v1beta2" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + troubleshootpreflight "github.com/replicatedhq/troubleshoot/pkg/preflight" k8sjson "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/client-go/kubernetes/scheme" ) @@ -578,17 +580,6 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) { } } - // installation spec gets updated during the process, ensure the map has the latest version - installationBytes, err := os.ReadFile(filepath.Join(u.GetUpstreamDir(writeUpstreamOptions), "userdata", "installation.yaml")) - if err != nil { - return "", errors.Wrap(err, "failed to read installation file") - } - renderedKotsKindsMap["userdata/installation.yaml"] = []byte(installationBytes) - - if err := kotsutil.WriteKotsKinds(renderedKotsKindsMap, u.GetKotsKindsDir(writeUpstreamOptions)); err != nil { - return "", errors.Wrap(err, "failed to write the rendered kots kinds") - } - if err := rendered.WriteRenderedApp(&rendered.WriteOptions{ BaseDir: u.GetBaseDir(writeUpstreamOptions), OverlaysDir: u.GetOverlaysDir(writeUpstreamOptions), @@ -604,6 +595,38 @@ func Pull(upstreamURI string, pullOptions PullOptions) (string, error) { return "", errors.Wrap(err, "failed to write rendered") } + // preflights may also be included within helm chart templates, so load any from the rendered dir + tsKinds, err := kotsutil.LoadTSKindsFromPath(u.GetRenderedDir(writeUpstreamOptions)) + if err != nil { + return "", errors.Wrap(err, fmt.Sprintf("failed to load troubleshoot kinds from path: %s", u.GetRenderedDir(writeUpstreamOptions))) + } + + if tsKinds.PreflightsV1Beta2 != nil { + var renderedPreflight *troubleshootv1beta2.Preflight + for _, v := range tsKinds.PreflightsV1Beta2 { + renderedPreflight = troubleshootpreflight.ConcatPreflightSpec(renderedPreflight, &v) + } + + if renderedPreflight != nil { + renderedPreflightBytes, err := kotsutil.MarshalRuntimeObject(renderedPreflight) + if err != nil { + return "", errors.Wrap(err, "failed to marshal rendered preflight") + } + renderedKotsKindsMap["helm-preflight.yaml"] = renderedPreflightBytes + } + } + + // installation spec gets updated during the process, ensure the map has the latest version + installationBytes, err := os.ReadFile(filepath.Join(u.GetUpstreamDir(writeUpstreamOptions), "userdata", "installation.yaml")) + if err != nil { + return "", errors.Wrap(err, "failed to read installation file") + } + renderedKotsKindsMap["userdata/installation.yaml"] = []byte(installationBytes) + + if err := kotsutil.WriteKotsKinds(renderedKotsKindsMap, u.GetKotsKindsDir(writeUpstreamOptions)); err != nil { + return "", errors.Wrap(err, "failed to write the rendered kots kinds") + } + return filepath.Join(pullOptions.RootDir, u.Name), nil } diff --git a/pkg/rewrite/rewrite.go b/pkg/rewrite/rewrite.go index dd77a670f8..45981575e2 100644 --- a/pkg/rewrite/rewrite.go +++ b/pkg/rewrite/rewrite.go @@ -25,6 +25,8 @@ import ( "github.com/replicatedhq/kots/pkg/upstream" upstreamtypes "github.com/replicatedhq/kots/pkg/upstream/types" kotsv1beta1 "github.com/replicatedhq/kotskinds/apis/kots/v1beta1" + troubleshootv1beta2 "github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta2" + troubleshootpreflight "github.com/replicatedhq/troubleshoot/pkg/preflight" ) type RewriteOptions struct { @@ -313,17 +315,6 @@ func Rewrite(rewriteOptions RewriteOptions) error { return errors.Wrap(err, "failed to update installation spec") } - // installation spec gets updated during the process, ensure the map has the latest version - installationBytes, err := os.ReadFile(filepath.Join(u.GetUpstreamDir(writeUpstreamOptions), "userdata", "installation.yaml")) - if err != nil { - return errors.Wrap(err, "failed to read installation file") - } - renderedKotsKindsMap["userdata/installation.yaml"] = installationBytes - - if err := kotsutil.WriteKotsKinds(renderedKotsKindsMap, u.GetKotsKindsDir(writeUpstreamOptions)); err != nil { - return errors.Wrap(err, "failed to write kots base") - } - if err := rendered.WriteRenderedApp(&rendered.WriteOptions{ BaseDir: u.GetBaseDir(writeUpstreamOptions), OverlaysDir: u.GetOverlaysDir(writeUpstreamOptions), @@ -339,6 +330,38 @@ func Rewrite(rewriteOptions RewriteOptions) error { return errors.Wrap(err, "failed to write rendered") } + // preflights may also be included within helm chart templates, so load any from the rendered dir + tsKinds, err := kotsutil.LoadTSKindsFromPath(u.GetRenderedDir(writeUpstreamOptions)) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to load troubleshoot kinds from path: %s", u.GetRenderedDir(writeUpstreamOptions))) + } + + if tsKinds.PreflightsV1Beta2 != nil { + var renderedPreflight *troubleshootv1beta2.Preflight + for _, v := range tsKinds.PreflightsV1Beta2 { + renderedPreflight = troubleshootpreflight.ConcatPreflightSpec(renderedPreflight, &v) + } + + if renderedPreflight != nil { + renderedPreflightBytes, err := kotsutil.MarshalRuntimeObject(renderedPreflight) + if err != nil { + return errors.Wrap(err, "failed to marshal rendered preflight") + } + renderedKotsKindsMap["helm-preflight.yaml"] = renderedPreflightBytes + } + } + + // installation spec gets updated during the process, ensure the map has the latest version + installationBytes, err := os.ReadFile(filepath.Join(u.GetUpstreamDir(writeUpstreamOptions), "userdata", "installation.yaml")) + if err != nil { + return errors.Wrap(err, "failed to read installation file") + } + renderedKotsKindsMap["userdata/installation.yaml"] = installationBytes + + if err := kotsutil.WriteKotsKinds(renderedKotsKindsMap, u.GetKotsKindsDir(writeUpstreamOptions)); err != nil { + return errors.Wrap(err, "failed to write the rendered kots kinds") + } + log.FinishSpinner() return nil diff --git a/pkg/tests/pull/cases/kotskinds/upstream/my-preflight-chart-0.1.0.tgz b/pkg/tests/pull/cases/kotskinds/upstream/my-preflight-chart-0.1.0.tgz new file mode 100644 index 0000000000..a43c1e8bad Binary files /dev/null and b/pkg/tests/pull/cases/kotskinds/upstream/my-preflight-chart-0.1.0.tgz differ diff --git a/pkg/tests/pull/cases/kotskinds/upstream/my-preflight-chart.yaml b/pkg/tests/pull/cases/kotskinds/upstream/my-preflight-chart.yaml new file mode 100644 index 0000000000..40a858c9bd --- /dev/null +++ b/pkg/tests/pull/cases/kotskinds/upstream/my-preflight-chart.yaml @@ -0,0 +1,11 @@ +# this chart validates that preflights inside helm chart templates are processed correctly as kotskinds +apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: my-preflight-chart + annotations: + kots.io/exclude: "true" +spec: + chart: + name: my-preflight-chart + chartVersion: 0.1.0 diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/helm/my-preflight-chart/my-preflight-chart-0.1.0.tgz b/pkg/tests/pull/cases/kotskinds/wantResults/helm/my-preflight-chart/my-preflight-chart-0.1.0.tgz new file mode 100644 index 0000000000..a43c1e8bad Binary files /dev/null and b/pkg/tests/pull/cases/kotskinds/wantResults/helm/my-preflight-chart/my-preflight-chart-0.1.0.tgz differ diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/helm/my-preflight-chart/values.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/helm/my-preflight-chart/values.yaml new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/pkg/tests/pull/cases/kotskinds/wantResults/helm/my-preflight-chart/values.yaml @@ -0,0 +1 @@ +{} diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/helm-preflight.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/helm-preflight.yaml new file mode 100644 index 0000000000..4aa57bd50b --- /dev/null +++ b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/helm-preflight.yaml @@ -0,0 +1,23 @@ +apiVersion: troubleshoot.sh/v1beta2 +kind: Preflight +metadata: + creationTimestamp: null + name: airgap-smoke-test-preflight +spec: + analyzers: + - clusterVersion: + outcomes: + - fail: + message: The application requires at Kubernetes 1.13.0 or later, and recommends + 1.15.0. + uri: https://www.kubernetes.io + when: < 1.13.0 + - warn: + message: Your cluster meets the minimum version of Kubernetes, but we recommend + you update to 1.15.0 or later. + uri: https://kubernetes.io + when: < 1.15.0 + - pass: + message: Your cluster meets the minimum version of Kubernetes recommended + by the application. +status: {} diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/my-preflight-chart.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/my-preflight-chart.yaml new file mode 100644 index 0000000000..40a858c9bd --- /dev/null +++ b/pkg/tests/pull/cases/kotskinds/wantResults/kotsKinds/my-preflight-chart.yaml @@ -0,0 +1,11 @@ +# this chart validates that preflights inside helm chart templates are processed correctly as kotskinds +apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: my-preflight-chart + annotations: + kots.io/exclude: "true" +spec: + chart: + name: my-preflight-chart + chartVersion: 0.1.0 diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/helm/my-preflight-chart/all.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/helm/my-preflight-chart/all.yaml new file mode 100644 index 0000000000..cab3760d2b --- /dev/null +++ b/pkg/tests/pull/cases/kotskinds/wantResults/rendered/this-cluster/helm/my-preflight-chart/all.yaml @@ -0,0 +1,29 @@ +--- +# Source: my-preflight-chart/templates/my-chart-preflight.yaml +apiVersion: v1 +kind: Secret +metadata: + labels: + troubleshoot.sh/kind: preflight + name: "my-preflight-chart-preflight-config" +stringData: + preflight.yaml: | + apiVersion: troubleshoot.sh/v1beta2 + kind: Preflight + metadata: + name: airgap-smoke-test-preflight + spec: + collectors: [] + analyzers: + - clusterVersion: + outcomes: + - fail: + when: "< 1.13.0" + message: The application requires at Kubernetes 1.13.0 or later, and recommends 1.15.0. + uri: https://www.kubernetes.io + - warn: + when: "< 1.15.0" + message: Your cluster meets the minimum version of Kubernetes, but we recommend you update to 1.15.0 or later. + uri: https://kubernetes.io + - pass: + message: Your cluster meets the minimum version of Kubernetes recommended by the application. diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/my-preflight-chart-0.1.0.tgz b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/my-preflight-chart-0.1.0.tgz new file mode 100644 index 0000000000..a43c1e8bad Binary files /dev/null and b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/my-preflight-chart-0.1.0.tgz differ diff --git a/pkg/tests/pull/cases/kotskinds/wantResults/upstream/my-preflight-chart.yaml b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/my-preflight-chart.yaml new file mode 100644 index 0000000000..40a858c9bd --- /dev/null +++ b/pkg/tests/pull/cases/kotskinds/wantResults/upstream/my-preflight-chart.yaml @@ -0,0 +1,11 @@ +# this chart validates that preflights inside helm chart templates are processed correctly as kotskinds +apiVersion: kots.io/v1beta2 +kind: HelmChart +metadata: + name: my-preflight-chart + annotations: + kots.io/exclude: "true" +spec: + chart: + name: my-preflight-chart + chartVersion: 0.1.0 diff --git a/web/src/features/AppConfig/components/AppConfig.tsx b/web/src/features/AppConfig/components/AppConfig.tsx index e9214d58f2..ff9057648d 100644 --- a/web/src/features/AppConfig/components/AppConfig.tsx +++ b/web/src/features/AppConfig/components/AppConfig.tsx @@ -141,8 +141,10 @@ class AppConfig extends Component { } window.addEventListener("resize", this.determineSidebarHeight); - if (!this.props.app) { - this.getApp(); + if (!app) { + this.fetchApp(); + } else { + this.setState({ app }); } this.getConfig(); } @@ -204,11 +206,7 @@ class AppConfig extends Component { } }; - getApp = async () => { - if (this.props.app) { - return; - } - + fetchApp = async (): Promise => { try { const { slug } = this.props.params; const res = await fetch(`${process.env.API_ENDPOINT}/app/${slug}`, { @@ -221,6 +219,7 @@ class AppConfig extends Component { if (res.ok && res.status == 200) { const app = await res.json(); this.setState({ app }); + return app; } } catch (err) { console.log(err); @@ -416,7 +415,8 @@ class AppConfig extends Component { } if (fromLicenseFlow) { - const hasPreflight = this.props.app.hasPreflight; + const app = await this.fetchApp(); + const hasPreflight = app?.hasPreflight; if (hasPreflight) { navigate(`/${slug}/preflight`, { replace: true }); @@ -661,6 +661,7 @@ class AppConfig extends Component { render() { const { + app, changed, showConfigError, configErrorMessage, @@ -675,7 +676,6 @@ class AppConfig extends Component { showValidationError, } = this.state; const { fromLicenseFlow, params, isHelmManaged } = this.props; - const app = this.props.app; if (configLoading || !app) { return (