Compare commits

...

1 Commits

Author SHA1 Message Date
Jeff McCune
6cc4a57b62 (#72) BuildPlan DisallowUnknownFields
This patch disallows unknown fields from CUE.  The purpose is to fail
early if there is a typo in a nested field name and to speed up
refactoring the reference platform.

With this patch, refactoring the type definition of the Holos/CUE API is
a faster process:

 1. Change api/vX/*.go
 2. make gencue
 3. Render the reference platform
 4. Fix error with unknown fields
 5. Verify rendered output is the same as before

Closes: #72
2024-03-22 12:44:11 -07:00
10 changed files with 51 additions and 24 deletions

View File

@@ -19,9 +19,9 @@ type BuildPlanSpec struct {
}
type BuildPlanComponents struct {
HelmCharts []HelmChart `json:"helmCharts,omitempty" yaml:"helmCharts,omitempty"`
KubernetesObjects []KubernetesObjects `json:"kubernetesObjects,omitempty" yaml:"kubernetesObjects,omitempty"`
KustomizeBuilds []KustomizeBuild `json:"kustomizeBuilds,omitempty" yaml:"kustomizeBuilds,omitempty"`
HelmChartList []HelmChart `json:"helmChartList,omitempty" yaml:"helmChartList,omitempty"`
KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty" yaml:"kubernetesObjectsList,omitempty"`
KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty" yaml:"kustomizeBuildList,omitempty"`
}
func (bp *BuildPlan) Validate() error {

View File

@@ -11,7 +11,7 @@ metadata: name: "jeff"
-- foo/bar/bar.cue --
package holos
spec: components: KubernetesObjects: [
spec: components: KubernetesObjectsList: [
#KubernetesObjects & {
apiObjectMap: foo: bar: "bf2bc7f9-9ba0-4f9e-9bd2-9a205627eb0b"
}
@@ -19,7 +19,7 @@ spec: components: KubernetesObjects: [
-- schema.cue --
package holos
cluster: string @tag(cluster, string)
_cluster: string @tag(cluster, string)
#KubernetesObjects: {
apiVersion: "holos.run/v1alpha1"

View File

@@ -1,6 +1,6 @@
# Want cue errors to show files and lines
! exec holos build .
stderr 'apiObjectMap.foo.bar: cannot convert non-concrete value string:'
stderr 'apiObjectMap.foo.bar: cannot convert incomplete value'
stderr '/component.cue:\d+:\d+$'
-- cue.mod --
@@ -8,9 +8,10 @@ package holos
-- component.cue --
package holos
_cluster: string @tag(cluster, string)
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"
cluster: string @tag(cluster, string)
spec: components: KubernetesObjectsList: [{apiObjectMap: foo: bar: _baz}]
baz: string
spec: components: KubernetesObjects: [{apiObjectMap: foo: bar: baz}]
_baz: string

View File

@@ -10,9 +10,9 @@ package holos
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"
spec: components: KubernetesObjects: [{apiObjectMap: #APIObjects.apiObjectMap}]
spec: components: KubernetesObjectsList: [{apiObjectMap: #APIObjects.apiObjectMap}]
cluster: string @tag(cluster, string)
_cluster: string @tag(cluster, string)
#SecretStore: {
kind: string

View File

@@ -11,9 +11,9 @@ package holos
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"
spec: components: HelmCharts: [{apiObjectMap: #APIObjects.apiObjectMap}]
spec: components: HelmChartList: [{apiObjectMap: #APIObjects.apiObjectMap}]
cluster: string @tag(cluster, string)
_cluster: string @tag(cluster, string)
#SecretStore: {
kind: string

View File

@@ -9,9 +9,9 @@ package holos
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"
spec: components: HelmCharts: [_HelmChart]
spec: components: HelmChartList: [_HelmChart]
cluster: string @tag(cluster, string)
_cluster: string @tag(cluster, string)
_HelmChart: {
apiVersion: "holos.run/v1alpha1"

View File

@@ -9,11 +9,11 @@ package holos
-- component.cue --
package holos
cluster: string @tag(cluster, string)
_cluster: string @tag(cluster, string)
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"
spec: components: KustomizeBuilds: [{metadata: name: "kstest"}]
spec: components: KustomizeBuildList: [{metadata: name: "kstest"}]
-- kustomization.yaml --
apiVersion: kustomize.config.k8s.io/v1beta1

View File

@@ -0,0 +1,14 @@
# https://github.com/holos-run/holos/issues/72
# Want holos to fail on unknown fields to catch typos and aid refactors
! exec holos build .
stderr 'unknown field \\"TypoKubernetesObjectsList\\"'
-- cue.mod --
package holos
-- component.cue --
package holos
_cluster: string @tag(cluster, string)
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"
spec: components: TypoKubernetesObjectsList: []

View File

@@ -4,7 +4,9 @@
package builder
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
@@ -129,13 +131,22 @@ func (b *Builder) Run(ctx context.Context) (results []*v1alpha1.Result, err erro
if err := value.Validate(); err != nil {
return nil, wrapper.Wrap(fmt.Errorf("could not validate: %w", err))
}
log.DebugContext(ctx, "cue: decoding holos build plan")
if err := value.Decode(&buildPlan); err != nil {
return nil, wrapper.Wrap(fmt.Errorf("could not decode: %w", err))
// Hack to catch unknown fields https://github.com/holos-run/holos/issues/72
jsonBytes, err := value.MarshalJSON()
if err != nil {
return nil, wrapper.Wrap(fmt.Errorf("could not marshal cue instance %s: %w", instance.Dir, err))
}
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
decoder.DisallowUnknownFields()
err = decoder.Decode(&buildPlan)
if err != nil {
return nil, wrapper.Wrap(fmt.Errorf("invalid BuildPlan: %s: %w", instance.Dir, err))
}
if err := buildPlan.Validate(); err != nil {
return nil, wrapper.Wrap(fmt.Errorf("could not render: %w", err))
return nil, wrapper.Wrap(fmt.Errorf("could not validate %s: %w", instance.Dir, err))
}
if buildPlan.Spec.Disabled {
@@ -143,21 +154,22 @@ func (b *Builder) Run(ctx context.Context) (results []*v1alpha1.Result, err erro
continue
}
for _, component := range buildPlan.Spec.Components.KubernetesObjects {
// TODO: concurrent renders
for _, component := range buildPlan.Spec.Components.KubernetesObjectsList {
if result, err := component.Render(ctx, holos.PathComponent(instance.Dir)); err != nil {
return nil, wrapper.Wrap(fmt.Errorf("could not render: %w", err))
} else {
results = append(results, result)
}
}
for _, component := range buildPlan.Spec.Components.HelmCharts {
for _, component := range buildPlan.Spec.Components.HelmChartList {
if result, err := component.Render(ctx, holos.PathComponent(instance.Dir)); err != nil {
return nil, wrapper.Wrap(fmt.Errorf("could not render: %w", err))
} else {
results = append(results, result)
}
}
for _, component := range buildPlan.Spec.Components.KustomizeBuilds {
for _, component := range buildPlan.Spec.Components.KustomizeBuildList {
if result, err := component.Render(ctx, holos.PathComponent(instance.Dir)); err != nil {
return nil, wrapper.Wrap(fmt.Errorf("could not render: %w", err))
} else {

View File

@@ -1 +1 @@
1
2