From bbcf280da7447a9000d1c53bb65deca327fddac1 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Fri, 28 Jun 2024 16:16:12 -0700 Subject: [PATCH 01/15] (#189) Refactor v1alpha2 API Previously methods were defined on the API objects in the v1alpha1 API. The API should be data structures only. This patch refactors the methods responsible for orchestrating the build plan to pull them into the internal render package. The result is the API is cleaner and has no methods. The render package has corresponding data structures which simply wrap around the API structure and implement the methods to render and return the result to the CLI. This commit compiles, but it has not been tested at all. It's almost surely broken completely. --- api/core/v1alpha2/buildplan.go | 118 ++++++++++ api/core/v1alpha2/constants.go | 11 + api/core/v1alpha2/helm.go | 37 +++ api/core/v1alpha2/kubernetesobjects.go | 10 + api/core/v1alpha2/kustomizebuild.go | 8 + api/v1alpha1/result.go | 5 - internal/builder/builder.go | 49 ++-- internal/cli/build/build.go | 2 +- internal/cli/render/render.go | 10 - .../api/core/v1alpha2/buildplan_go_gen.cue | 98 ++++++++ .../api/core/v1alpha2/constants_go_gen.cue | 15 ++ .../holos/api/core/v1alpha2/helm_go_gen.cue | 46 ++++ .../v1alpha2/kubernetesobjects_go_gen.cue | 14 ++ .../core/v1alpha2/kustomizebuild_go_gen.cue | 12 + .../generate/platforms/holos/buildplan.cue | 18 +- internal/render/component.go | 52 +++++ internal/render/helm.go | 171 ++++++++++++++ internal/render/result.go | 215 ++++++++++++++++++ 18 files changed, 842 insertions(+), 49 deletions(-) create mode 100644 api/core/v1alpha2/buildplan.go create mode 100644 api/core/v1alpha2/constants.go create mode 100644 api/core/v1alpha2/helm.go create mode 100644 api/core/v1alpha2/kubernetesobjects.go create mode 100644 api/core/v1alpha2/kustomizebuild.go create mode 100644 internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue create mode 100644 internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue create mode 100644 internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue create mode 100644 internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kubernetesobjects_go_gen.cue create mode 100644 internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kustomizebuild_go_gen.cue create mode 100644 internal/render/component.go create mode 100644 internal/render/helm.go create mode 100644 internal/render/result.go diff --git a/api/core/v1alpha2/buildplan.go b/api/core/v1alpha2/buildplan.go new file mode 100644 index 00000000..ff8d12f8 --- /dev/null +++ b/api/core/v1alpha2/buildplan.go @@ -0,0 +1,118 @@ +package v1alpha2 + +import ( + "fmt" + "strings" +) + +// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking. +type Label string + +// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking. +type Kind string + +// APIObjectMap is the shape of marshalled api objects returned from cue to the +// holos cli. A map is used to improve the clarity of error messages from cue +// relative to a list. +type APIObjectMap map[Kind]map[Label]string + +// FileContentMap represents a mapping of file names to file content. +type FileContentMap map[string]string + +// BuildPlan represents a build plan for the holos cli to execute. A build plan +// is a set of zero or more holos components. +type BuildPlan struct { + // Kind is a string value representing the resource this object represents. + Kind string `json:"kind" yaml:"kind" cue:"\"BuildPlan\""` + // APIVersion represents the versioned schema of this representation of an object. + APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha2\""` + // Spec represents the specification. + Spec BuildPlanSpec `json:"spec" yaml:"spec"` +} + +func (bp *BuildPlan) Validate() error { + errs := make([]string, 0, 2) + if bp.Kind != BuildPlanKind { + errs = append(errs, fmt.Sprintf("kind invalid: want: %s have: %s", BuildPlanKind, bp.Kind)) + } + if bp.APIVersion != APIVersion { + errs = append(errs, fmt.Sprintf("apiVersion invalid: want: %s have: %s", APIVersion, bp.APIVersion)) + } + if len(errs) > 0 { + return fmt.Errorf("invalid BuildPlan: " + strings.Join(errs, ", ")) + } + return nil +} + +func (bp *BuildPlan) ResultCapacity() (count int) { + if bp == nil { + return 0 + } + count = len(bp.Spec.Components.HelmChartList) + // +len(bp.Spec.Components.KubernetesObjectsList) + // +len(bp.Spec.Components.KustomizeBuildList) + // +len(bp.Spec.Components.Resources) + return count +} + +type BuildPlanSpec struct { + Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"` + Components BuildPlanComponents `json:"components,omitempty" yaml:"components,omitempty"` +} + +type BuildPlanComponents struct { + Resources map[string]KubernetesObjects `json:"resources,omitempty" yaml:"resources,omitempty"` + KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty" yaml:"kubernetesObjectsList,omitempty"` + HelmChartList []HelmChart `json:"helmChartList,omitempty" yaml:"helmChartList,omitempty"` + KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty" yaml:"kustomizeBuildList,omitempty"` +} + +// HolosComponent defines the fields common to all holos component kinds. Every +// holos component kind should embed HolosComponent. +type HolosComponent struct { + // Kind is a string value representing the resource this object represents. + Kind string `json:"kind" yaml:"kind"` + // APIVersion represents the versioned schema of this representation of an object. + APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha2\""` + // Metadata represents data about the holos component such as the Name. + Metadata Metadata `json:"metadata" yaml:"metadata"` + + // APIObjectMap holds the marshalled representation of api objects. Think of + // these objects as being mixed into the upstream resources, for example + // adding an ExternalSecret to a rendered Helm chart. + APIObjectMap APIObjectMap `json:"apiObjectMap,omitempty" yaml:"apiObjectMap,omitempty"` + + // DeployFiles represents file paths relative to the cluster deploy directory + // with the value representing the file content. Intended for defining the + // ArgoCD Application resource or Flux Kustomization resource from within CUE, + // but may be used to render any file related to the build plan from CUE. + DeployFiles FileContentMap `json:"deployFiles,omitempty" yaml:"deployFiles,omitempty"` + + // Kustomize represents a kubectl kustomize build post-processing step. + Kustomize `json:"kustomize,omitempty" yaml:"kustomize,omitempty"` + + // Skip causes holos to take no action regarding this component. + Skip bool +} + +// Metadata represents data about the holos component such as the Name. +type Metadata struct { + // Name represents the name of the holos component. + Name string `json:"name" yaml:"name"` + // Namespace is the primary namespace of the holos component. A holos + // component may manage resources in multiple namespaces, in this case + // consider setting the component namespace to default. + Namespace string `json:"namespace" yaml:"namespace"` +} + +// Kustomize represents resources necessary to execute a kustomize build. +// Intended for at least two use cases: +// +// 1. Process raw yaml file resources in a holos component directory. +// 2. Post process a HelmChart to inject istio, add custom labels, etc... +type Kustomize struct { + // KustomizeFiles holds file contents for kustomize, e.g. patch files. + KustomizeFiles FileContentMap `json:"kustomizeFiles,omitempty" yaml:"kustomizeFiles,omitempty"` + // ResourcesFile is the file name used for api objects in kustomization.yaml + ResourcesFile string `json:"resourcesFile,omitempty" yaml:"resourcesFile,omitempty"` +} diff --git a/api/core/v1alpha2/constants.go b/api/core/v1alpha2/constants.go new file mode 100644 index 00000000..caa921e7 --- /dev/null +++ b/api/core/v1alpha2/constants.go @@ -0,0 +1,11 @@ +package v1alpha2 + +const ( + APIVersion = "holos.run/v1alpha2" + BuildPlanKind = "BuildPlan" + HelmChartKind = "HelmChart" + // ChartDir is the directory name created in the holos component directory to cache a chart. + ChartDir = "vendor" + // ResourcesFile is the file name used to store component output when post-processing with kustomize. + ResourcesFile = "resources.yaml" +) diff --git a/api/core/v1alpha2/helm.go b/api/core/v1alpha2/helm.go new file mode 100644 index 00000000..5928b92b --- /dev/null +++ b/api/core/v1alpha2/helm.go @@ -0,0 +1,37 @@ +package v1alpha2 + +// HelmChart represents a holos component which wraps around an upstream helm +// chart. Holos orchestrates helm by providing values obtained from CUE, +// renders the output using `helm template`, then post-processes the helm output +// yaml using the general functionality provided by HolosComponent, for example +// kustomize post-rendering and mixing in additional kubernetes api objects. +type HelmChart struct { + HolosComponent `json:",inline" yaml:",inline"` + Kind string `json:"kind" yaml:"kind" cue:"\"HelmChart\""` + + // Chart represents the helm Chart. + Chart Chart `json:"chart"` + // ValuesContent represents the values.yaml file holos passes to the `helm + // template` command. + ValuesContent string `json:"valuesContent"` + // EnableHooks enables helm hooks when executing the `helm template` command. + EnableHooks bool `json:"enableHooks"` +} + +// Chart represents the helm Chart. +type Chart struct { + // Name represents the chart name. + Name string `json:"name"` + // Version represents the chart version. + Version string `json:"version"` + // Release represents the chart release when executing helm template. + Release string `json:"release"` + // Repository represents the repository to fetch the chart from. + Repository Repository `json:"repository,omitempty"` +} + +// Repository represents a helm chart repository. +type Repository struct { + Name string `json:"name"` + URL string `json:"url"` +} diff --git a/api/core/v1alpha2/kubernetesobjects.go b/api/core/v1alpha2/kubernetesobjects.go new file mode 100644 index 00000000..2cacce00 --- /dev/null +++ b/api/core/v1alpha2/kubernetesobjects.go @@ -0,0 +1,10 @@ +package v1alpha2 + +const KubernetesObjectsKind = "KubernetesObjects" + +// KubernetesObjects represents a holos component composed of kubernetes api +// objects provided directly from CUE. +type KubernetesObjects struct { + HolosComponent `json:",inline" yaml:",inline"` + Kind string `json:"kind" yaml:"kind" cue:"\"KubernetesObjects\""` +} diff --git a/api/core/v1alpha2/kustomizebuild.go b/api/core/v1alpha2/kustomizebuild.go new file mode 100644 index 00000000..ad9a376b --- /dev/null +++ b/api/core/v1alpha2/kustomizebuild.go @@ -0,0 +1,8 @@ +package v1alpha2 + +// KustomizeBuild renders plain yaml files in the holos component directory +// using kubectl kustomize build. +type KustomizeBuild struct { + HolosComponent `json:",inline" yaml:",inline"` + Kind string `json:"kind" yaml:"kind" cue:"\"KustomizeBuild\""` +} diff --git a/api/v1alpha1/result.go b/api/v1alpha1/result.go index 0eb208c1..ff286adc 100644 --- a/api/v1alpha1/result.go +++ b/api/v1alpha1/result.go @@ -44,11 +44,6 @@ func (r *Result) KustomizationFilename(writeTo string, cluster string) string { return filepath.Join(writeTo, "clusters", cluster, "holos", "components", r.Metadata.Name+"-kustomization.gen.yaml") } -// KustomizationContent returns the kustomization file contents to write. -func (r *Result) KustomizationContent() string { - return r.KsContent -} - // AccumulatedOutput returns the accumulated rendered output. func (r *Result) AccumulatedOutput() string { return r.accumulatedOutput diff --git a/internal/builder/builder.go b/internal/builder/builder.go index 9bcd1cf7..fc642fba 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -15,22 +15,25 @@ import ( "cuelang.org/go/cue/build" "cuelang.org/go/cue/cuecontext" "cuelang.org/go/cue/load" + "github.com/holos-run/holos/api/core/v1alpha2" "github.com/holos-run/holos/api/v1alpha1" "github.com/holos-run/holos" "github.com/holos-run/holos/internal/client" "github.com/holos-run/holos/internal/errors" "github.com/holos-run/holos/internal/logger" + "github.com/holos-run/holos/internal/render" ) const ( - KubernetesObjects = v1alpha1.KubernetesObjectsKind + KubernetesObjects = v1alpha2.KubernetesObjectsKind // Helm is the value of the kind field of holos build output indicating helm // values and helm command information. - Helm = v1alpha1.HelmChartKind + Helm = v1alpha2.HelmChartKind // Skip is the value when the instance should be skipped Skip = "Skip" - // KustomizeBuild is the value of the kind field of cue output indicating holos should process the component using kustomize build to render output. + // KustomizeBuild is the value of the kind field of cue output indicating + // holos should process the component using kustomize build to render output. KustomizeBuild = v1alpha1.KustomizeBuildKind ) @@ -128,14 +131,14 @@ func (b *Builder) Instances(ctx context.Context, cfg *client.Config) ([]*build.I return load.Instances(args, &cueConfig), nil } -func (b *Builder) Run(ctx context.Context, cfg *client.Config) (results []*v1alpha1.Result, err error) { +func (b *Builder) Run(ctx context.Context, cfg *client.Config) (results []*render.Result, err error) { log := logger.FromContext(ctx) log.DebugContext(ctx, "cue: building instances") instances, err := b.Instances(ctx, cfg) if err != nil { return nil, err } - results = make([]*v1alpha1.Result, 0, len(instances)*8) + results = make([]*render.Result, 0, len(instances)*8) // Each CUE instance provides a BuildPlan for idx, instance := range instances { @@ -150,7 +153,7 @@ func (b *Builder) Run(ctx context.Context, cfg *client.Config) (results []*v1alp return results, nil } -func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (results []*v1alpha1.Result, err error) { +func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (results []*render.Result, err error) { path := holos.InstancePath(instance.Dir) log := logger.FromContext(ctx).With("dir", path) @@ -191,9 +194,8 @@ func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (res decoder.DisallowUnknownFields() switch tm.Kind { - // TODO(jeff) Process a v1alpha1.Result here, the result is tightly coupled to a BuildPlan. case "BuildPlan": - var bp v1alpha1.BuildPlan + var bp v1alpha2.BuildPlan if err = decoder.Decode(&bp); err != nil { err = errors.Wrap(fmt.Errorf("could not decode BuildPlan %s: %w", instance.Dir, err)) return @@ -209,7 +211,7 @@ func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (res return results, err } -func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha1.BuildPlan, path holos.InstancePath) (results []*v1alpha1.Result, err error) { +func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha2.BuildPlan, path holos.InstancePath) (results []*render.Result, err error) { log := logger.FromContext(ctx) if err := buildPlan.Validate(); err != nil { @@ -222,11 +224,12 @@ func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha1.BuildPlan, return } - // TODO: concurrent renders - results = make([]*v1alpha1.Result, 0, buildPlan.ResultCapacity()) + results = make([]*render.Result, 0, buildPlan.ResultCapacity()) log.DebugContext(ctx, "allocated results slice", "cap", buildPlan.ResultCapacity()) + for _, component := range buildPlan.Spec.Components.Resources { - if result, err := component.Render(ctx, path); err != nil { + ko := render.KubernetesObjects{Component: component} + if result, err := ko.Render(ctx, path); err != nil { return nil, errors.Wrap(fmt.Errorf("could not render: %w", err)) } else { results = append(results, result) @@ -234,21 +237,24 @@ func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha1.BuildPlan, } for _, component := range buildPlan.Spec.Components.KubernetesObjectsList { - if result, err := component.Render(ctx, path); err != nil { + ko := render.KubernetesObjects{Component: component} + if result, err := ko.Render(ctx, path); err != nil { return nil, errors.Wrap(fmt.Errorf("could not render: %w", err)) } else { results = append(results, result) } } for _, component := range buildPlan.Spec.Components.HelmChartList { - if result, err := component.Render(ctx, path); err != nil { + hc := render.HelmChart{Component: component} + if result, err := hc.Render(ctx, path); err != nil { return nil, errors.Wrap(fmt.Errorf("could not render: %w", err)) } else { results = append(results, result) } } for _, component := range buildPlan.Spec.Components.KustomizeBuildList { - if result, err := component.Render(ctx, path); err != nil { + kb := render.KustomizeBuild{Component: component} + if result, err := kb.Render(ctx, path); err != nil { return nil, errors.Wrap(fmt.Errorf("could not render: %w", err)) } else { results = append(results, result) @@ -256,15 +262,10 @@ func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha1.BuildPlan, } // Add a separate Result if there are DeployFiles from the BuildPlan. - if len(buildPlan.Spec.DeployFiles) > 0 { - results = append(results, &v1alpha1.Result{ - HolosComponent: v1alpha1.HolosComponent{ - TypeMeta: buildPlan.TypeMeta, - Metadata: buildPlan.Metadata, - }, - DeployFiles: buildPlan.Spec.DeployFiles, - }) - } + log.WarnContext(ctx, "TODO: Handle DeployFiles for each holos component.") + // if len(buildPlan.Spec.DeployFiles) > 0 { + // results = append(results, render.NewResult(buildPlan.Metadata.Name)) + // } log.DebugContext(ctx, "returning results", "len", len(results)) diff --git a/internal/cli/build/build.go b/internal/cli/build/build.go index 503fb2d6..5822dc9a 100644 --- a/internal/cli/build/build.go +++ b/internal/cli/build/build.go @@ -26,7 +26,7 @@ func makeBuildRunFunc(cfg *client.Config) command.RunFunc { } outs := make([]string, 0, len(results)) for idx, result := range results { - if result == nil || result.Skip { + if result.Continue() { slog.Debug("skip result", "idx", idx, "result", result) continue } diff --git a/internal/cli/render/render.go b/internal/cli/render/render.go index 7379be24..31c87a4c 100644 --- a/internal/cli/render/render.go +++ b/internal/cli/render/render.go @@ -88,15 +88,6 @@ func NewComponent(cfg *holos.Config) *cobra.Command { if err := result.Save(ctx, path, result.AccumulatedOutput()); err != nil { return errors.Wrap(err) } - // Kustomization - if result.KustomizationContent() == "" { - log.DebugContext(ctx, "flux kustomization: skipped "+result.Name(), "status", "ok", "action", "skipped") - } else { - path = result.KustomizationFilename(cfg.WriteTo(), cfg.ClusterName()) - if err := result.Save(ctx, path, result.KustomizationContent()); err != nil { - return errors.Wrap(err) - } - } log.InfoContext(ctx, "rendered "+result.Name(), "status", "ok", "action", "rendered") } @@ -139,7 +130,6 @@ type Result interface { KustomizationFilename(writeTo string, cluster string) string Save(ctx context.Context, path string, content string) error AccumulatedOutput() string - KustomizationContent() string WriteDeployFiles(ctx context.Context, writeTo string) error GetKind() string GetAPIVersion() string diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue new file mode 100644 index 00000000..db0c2d6a --- /dev/null +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue @@ -0,0 +1,98 @@ +// Code generated by cue get go. DO NOT EDIT. + +//cue:generate cue get go github.com/holos-run/holos/api/core/v1alpha2 + +package v1alpha2 + +// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking. +#Label: string + +// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking. +#Kind: string + +// APIObjectMap is the shape of marshalled api objects returned from cue to the +// holos cli. A map is used to improve the clarity of error messages from cue +// relative to a list. +#APIObjectMap: {[string]: [string]: string} + +// FileContentMap represents a mapping of file names to file content. +#FileContentMap: {[string]: string} + +// BuildPlan represents a build plan for the holos cli to execute. A build plan +// is a set of zero or more holos components. +#BuildPlan: { + // Kind is a string value representing the resource this object represents. + kind: string & "BuildPlan" @go(Kind) + + // APIVersion represents the versioned schema of this representation of an object. + apiVersion: string & (string | *"v1alpha2") @go(APIVersion) + + // Spec represents the specification. + spec: #BuildPlanSpec @go(Spec) +} + +#BuildPlanSpec: { + disabled?: bool @go(Disabled) + components?: #BuildPlanComponents @go(Components) +} + +#BuildPlanComponents: { + resources?: {[string]: #KubernetesObjects} @go(Resources,map[string]KubernetesObjects) + kubernetesObjectsList?: [...#KubernetesObjects] @go(KubernetesObjectsList,[]KubernetesObjects) + helmChartList?: [...#HelmChart] @go(HelmChartList,[]HelmChart) + kustomizeBuildList?: [...#KustomizeBuild] @go(KustomizeBuildList,[]KustomizeBuild) +} + +// HolosComponent defines the fields common to all holos component kinds. Every +// holos component kind should embed HolosComponent. +#HolosComponent: { + // Kind is a string value representing the resource this object represents. + kind: string @go(Kind) + + // APIVersion represents the versioned schema of this representation of an object. + apiVersion: string & (string | *"v1alpha2") @go(APIVersion) + + // Metadata represents data about the holos component such as the Name. + metadata: #Metadata @go(Metadata) + + // APIObjectMap holds the marshalled representation of api objects. Think of + // these objects as being mixed into the upstream resources, for example + // adding an ExternalSecret to a rendered Helm chart. + apiObjectMap?: #APIObjectMap @go(APIObjectMap) + + // DeployFiles represents file paths relative to the cluster deploy directory + // with the value representing the file content. Intended for defining the + // ArgoCD Application resource or Flux Kustomization resource from within CUE, + // but may be used to render any file related to the build plan from CUE. + deployFiles?: #FileContentMap @go(DeployFiles) + + // Kustomize represents a kubectl kustomize build post-processing step. + kustomize?: #Kustomize @go(Kustomize) + + // Skip causes holos to take no action regarding this component. + Skip: bool +} + +// Metadata represents data about the holos component such as the Name. +#Metadata: { + // Name represents the name of the holos component. + name: string @go(Name) + + // Namespace is the primary namespace of the holos component. A holos + // component may manage resources in multiple namespaces, in this case + // consider setting the component namespace to default. + namespace: string @go(Namespace) +} + +// Kustomize represents resources necessary to execute a kustomize build. +// Intended for at least two use cases: +// +// 1. Process raw yaml file resources in a holos component directory. +// 2. Post process a HelmChart to inject istio, add custom labels, etc... +#Kustomize: { + // KustomizeFiles holds file contents for kustomize, e.g. patch files. + kustomizeFiles?: #FileContentMap @go(KustomizeFiles) + + // ResourcesFile is the file name used for api objects in kustomization.yaml + resourcesFile?: string @go(ResourcesFile) +} diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue new file mode 100644 index 00000000..d9256bd7 --- /dev/null +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue @@ -0,0 +1,15 @@ +// Code generated by cue get go. DO NOT EDIT. + +//cue:generate cue get go github.com/holos-run/holos/api/core/v1alpha2 + +package v1alpha2 + +#APIVersion: "holos.run/v1alpha2" +#BuildPlanKind: "BuildPlan" +#HelmChartKind: "HelmChart" + +// ChartDir is the directory name created in the holos component directory to cache a chart. +#ChartDir: "vendor" + +// ResourcesFile is the file name used to store component output when post-processing with kustomize. +#ResourcesFile: "resources.yaml" diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue new file mode 100644 index 00000000..d1da1e22 --- /dev/null +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue @@ -0,0 +1,46 @@ +// Code generated by cue get go. DO NOT EDIT. + +//cue:generate cue get go github.com/holos-run/holos/api/core/v1alpha2 + +package v1alpha2 + +// HelmChart represents a holos component which wraps around an upstream helm +// chart. Holos orchestrates helm by providing values obtained from CUE, +// renders the output using `helm template`, then post-processes the helm output +// yaml using the general functionality provided by HolosComponent, for example +// kustomize post-rendering and mixing in additional kubernetes api objects. +#HelmChart: { + #HolosComponent + kind: string & "HelmChart" @go(Kind) + + // Chart represents the helm Chart. + chart: #Chart @go(Chart) + + // ValuesContent represents the values.yaml file holos passes to the `helm + // template` command. + valuesContent: string @go(ValuesContent) + + // EnableHooks enables helm hooks when executing the `helm template` command. + enableHooks: bool @go(EnableHooks) +} + +// Chart represents the helm Chart. +#Chart: { + // Name represents the chart name. + name: string @go(Name) + + // Version represents the chart version. + version: string @go(Version) + + // Release represents the chart release when executing helm template. + release: string @go(Release) + + // Repository represents the repository to fetch the chart from. + repository?: #Repository @go(Repository) +} + +// Repository represents a helm chart repository. +#Repository: { + name: string @go(Name) + url: string @go(URL) +} diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kubernetesobjects_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kubernetesobjects_go_gen.cue new file mode 100644 index 00000000..5b3828ef --- /dev/null +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kubernetesobjects_go_gen.cue @@ -0,0 +1,14 @@ +// Code generated by cue get go. DO NOT EDIT. + +//cue:generate cue get go github.com/holos-run/holos/api/core/v1alpha2 + +package v1alpha2 + +#KubernetesObjectsKind: "KubernetesObjects" + +// KubernetesObjects represents a holos component composed of kubernetes api +// objects provided directly from CUE. +#KubernetesObjects: { + #HolosComponent + kind: string & "KubernetesObjects" @go(Kind) +} diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kustomizebuild_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kustomizebuild_go_gen.cue new file mode 100644 index 00000000..b5ab8097 --- /dev/null +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/kustomizebuild_go_gen.cue @@ -0,0 +1,12 @@ +// Code generated by cue get go. DO NOT EDIT. + +//cue:generate cue get go github.com/holos-run/holos/api/core/v1alpha2 + +package v1alpha2 + +// KustomizeBuild renders plain yaml files in the holos component directory +// using kubectl kustomize build. +#KustomizeBuild: { + #HolosComponent + kind: string & "KustomizeBuild" @go(Kind) +} diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index 58184774..62223842 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -2,7 +2,7 @@ package holos import ( "encoding/yaml" - v1 "github.com/holos-run/holos/api/v1alpha1" + core "github.com/holos-run/holos/api/core/v1alpha2" kc "sigs.k8s.io/kustomize/api/types" @@ -103,7 +103,7 @@ import ( Values: {...} - Chart: v1.#HelmChart & { + Chart: core.#HelmChart & { metadata: name: string | *Name namespace: string | *Namespace chart: name: string | *Name @@ -119,15 +119,15 @@ import ( // resourcesFile represents the file helm output is written two and // kustomize reads from. Typically "resources.yaml" but referenced as a // constant to ensure the holos cli uses the same file. - resourcesFile: v1.#ResourcesFile + resourcesFile: core.#ResourcesFile // kustomizeFiles represents the files in a kustomize directory tree. - kustomizeFiles: v1.#FileContentMap + kustomizeFiles: core.#FileContentMap for FileName, Object in KustomizeFiles { kustomizeFiles: "\(FileName)": yaml.Marshal(Object) } } - apiObjectMap: (v1.#APIObjects & {apiObjects: Resources}).apiObjectMap + apiObjectMap: (core.#APIObjects & {apiObjects: Resources}).apiObjectMap } // EnableKustomizePostProcessor processes helm output with kustomize if true. @@ -147,7 +147,7 @@ import ( "kustomization.yaml": kc.#Kustomization & { apiVersion: "kustomize.config.k8s.io/v1beta1" kind: "Kustomization" - resources: [v1.#ResourcesFile, for FileName, _ in KustomizeResources {FileName}] + resources: [core.#ResourcesFile, for FileName, _ in KustomizeResources {FileName}] patches: [for x in KustomizePatches {x}] } } @@ -170,7 +170,7 @@ import ( // Name represents the holos component name Name: string - Kustomization: v1.#KustomizeBuild & { + Kustomization: core.#KustomizeBuild & { metadata: name: string | *Name } @@ -195,12 +195,12 @@ import ( // resources is a map unlike other build plans which use a list. spec: components: resources: "\(Name)": { metadata: name: Name - apiObjectMap: (v1.#APIObjects & {apiObjects: Resources}).apiObjectMap + apiObjectMap: (core.#APIObjects & {apiObjects: Resources}).apiObjectMap } } } -#BuildPlan: v1.#BuildPlan & { +#BuildPlan: core.#BuildPlan & { metadata: name: string // Render the ArgoCD Application spec: deployFiles: (#Argo & {ComponentName: metadata.name}).deployFiles diff --git a/internal/render/component.go b/internal/render/component.go new file mode 100644 index 00000000..4fb88331 --- /dev/null +++ b/internal/render/component.go @@ -0,0 +1,52 @@ +package render + +import ( + "context" + + "github.com/holos-run/holos" + "github.com/holos-run/holos/api/core/v1alpha2" + "github.com/holos-run/holos/internal/errors" + "github.com/holos-run/holos/internal/server/middleware/logger" + "github.com/holos-run/holos/internal/util" +) + +const KubernetesObjectsKind = "KubernetesObjects" + +// KubernetesObjects represents CUE output which directly provides Kubernetes api objects to holos. +type KubernetesObjects struct { + Component v1alpha2.KubernetesObjects `json:"component" yaml:"component"` +} + +// Render produces kubernetes api objects from the APIObjectMap of the holos component. +func (o *KubernetesObjects) Render(ctx context.Context, path holos.InstancePath) (*Result, error) { + result := NewResult(o.Component.HolosComponent) + result.addObjectMap(ctx, o.Component.APIObjectMap) + return result, nil +} + +// KustomizeBuild renders plain yaml files in the holos component directory +// using kubectl kustomize build. +type KustomizeBuild struct { + Component v1alpha2.KustomizeBuild `json:"component" yaml:"component"` +} + +// Render produces a Result by executing kubectl kustomize on the holos +// component path. Useful for processing raw yaml files. +func (kb *KustomizeBuild) Render(ctx context.Context, path holos.InstancePath) (*Result, error) { + if kb == nil { + return nil, nil + } + log := logger.FromContext(ctx) + result := NewResult(kb.Component.HolosComponent) + // Run kustomize. + kOut, err := util.RunCmd(ctx, "kubectl", "kustomize", string(path)) + if err != nil { + log.ErrorContext(ctx, kOut.Stderr.String()) + return nil, errors.Wrap(err) + } + // Replace the accumulated output + result.accumulatedOutput = kOut.Stdout.String() + // Add CUE based api objects. + result.addObjectMap(ctx, kb.Component.APIObjectMap) + return result, nil +} diff --git a/internal/render/helm.go b/internal/render/helm.go new file mode 100644 index 00000000..dc93fc80 --- /dev/null +++ b/internal/render/helm.go @@ -0,0 +1,171 @@ +package render + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + "syscall" + + "github.com/holos-run/holos" + "github.com/holos-run/holos/api/core/v1alpha2" + core "github.com/holos-run/holos/api/core/v1alpha2" + "github.com/holos-run/holos/internal/errors" + "github.com/holos-run/holos/internal/server/middleware/logger" + "github.com/holos-run/holos/internal/util" +) + +type HelmChart struct { + Component core.HelmChart `json:"component"` +} + +func (hc *HelmChart) Render(ctx context.Context, path holos.InstancePath) (*Result, error) { + if hc == nil { + return nil, nil + } + result := NewResult(hc.Component.HolosComponent) + if err := hc.helm(ctx, result, path); err != nil { + return nil, err + } + result.addObjectMap(ctx, hc.Component.APIObjectMap) + if err := result.kustomize(ctx); err != nil { + return nil, errors.Wrap(fmt.Errorf("could not kustomize: %w", err)) + } + return result, nil +} + +// runHelm provides the values produced by CUE to helm template and returns +// the rendered kubernetes api objects in the result. +func (hc *HelmChart) helm(ctx context.Context, r *Result, path holos.InstancePath) error { + log := logger.FromContext(ctx).With("chart", hc.Component.Chart.Name) + if hc.Component.Chart.Name == "" { + log.WarnContext(ctx, "skipping helm: no chart name specified, use a different component type") + return nil + } + + cachedChartPath := filepath.Join(string(path), v1alpha2.ChartDir, filepath.Base(hc.Component.Chart.Name)) + if isNotExist(cachedChartPath) { + // Add repositories + repo := hc.Component.Chart.Repository + if repo.URL != "" { + out, err := util.RunCmd(ctx, "helm", "repo", "add", repo.Name, repo.URL) + if err != nil { + log.ErrorContext(ctx, "could not run helm", "stderr", out.Stderr.String(), "stdout", out.Stdout.String()) + return errors.Wrap(fmt.Errorf("could not run helm repo add: %w", err)) + } + // Update repository + out, err = util.RunCmd(ctx, "helm", "repo", "update", repo.Name) + if err != nil { + log.ErrorContext(ctx, "could not run helm", "stderr", out.Stderr.String(), "stdout", out.Stdout.String()) + return errors.Wrap(fmt.Errorf("could not run helm repo update: %w", err)) + } + } else { + log.DebugContext(ctx, "no chart repository url proceeding assuming oci chart") + } + + // Cache the chart + if err := cacheChart(ctx, path, v1alpha2.ChartDir, hc.Component.Chart); err != nil { + return fmt.Errorf("could not cache chart: %w", err) + } + } + + // Write values file + tempDir, err := os.MkdirTemp("", "holos") + if err != nil { + return errors.Wrap(fmt.Errorf("could not make temp dir: %w", err)) + } + defer util.Remove(ctx, tempDir) + + valuesPath := filepath.Join(tempDir, "values.yaml") + if err := os.WriteFile(valuesPath, []byte(hc.Component.ValuesContent), 0644); err != nil { + return errors.Wrap(fmt.Errorf("could not write values: %w", err)) + } + log.DebugContext(ctx, "helm: wrote values", "path", valuesPath, "bytes", len(hc.Component.ValuesContent)) + + // Run charts + chart := hc.Component.Chart + args := []string{"template"} + if !hc.Component.EnableHooks { + args = append(args, "--no-hooks") + } + namespace := hc.Component.Metadata.Namespace + args = append(args, "--include-crds", "--values", valuesPath, "--namespace", namespace, "--kubeconfig", "/dev/null", "--version", chart.Version, chart.Release, cachedChartPath) + helmOut, err := util.RunCmd(ctx, "helm", args...) + if err != nil { + stderr := helmOut.Stderr.String() + lines := strings.Split(stderr, "\n") + for _, line := range lines { + if strings.HasPrefix(line, "Error:") { + err = fmt.Errorf("%s: %w", line, err) + } + } + return errors.Wrap(fmt.Errorf("could not run helm template: %w", err)) + } + + r.accumulatedOutput = helmOut.Stdout.String() + + return nil +} + +// cacheChart stores a cached copy of Chart in the chart subdirectory of path. +// +// It is assumed that the only method responsible for writing to chartDir is +// cacheChart itself. +// +// This relies on the atomicity of moving temporary directories into place on +// the same filesystem via os.Rename. If a syscall.EEXIST error occurs during +// renaming, it indicates that the cached chart already exists, which is an +// expected scenario when this function is called concurrently. +func cacheChart(ctx context.Context, path holos.InstancePath, chartDir string, chart v1alpha2.Chart) error { + log := logger.FromContext(ctx) + + cacheTemp, err := os.MkdirTemp(string(path), chartDir) + if err != nil { + return errors.Wrap(fmt.Errorf("could not make temp dir: %w", err)) + } + defer util.Remove(ctx, cacheTemp) + + chartName := chart.Name + if chart.Repository.Name != "" { + chartName = fmt.Sprintf("%s/%s", chart.Repository.Name, chart.Name) + } + helmOut, err := util.RunCmd(ctx, "helm", "pull", "--destination", cacheTemp, "--untar=true", "--version", chart.Version, chartName) + if err != nil { + return errors.Wrap(fmt.Errorf("could not run helm pull: %w", err)) + } + log.Debug("helm pull", "stdout", helmOut.Stdout, "stderr", helmOut.Stderr) + + cachePath := filepath.Join(string(path), chartDir) + + if err := os.MkdirAll(cachePath, 0777); err != nil { + return errors.Wrap(fmt.Errorf("could not mkdir: %w", err)) + } + + items, err := os.ReadDir(cacheTemp) + if err != nil { + return errors.Wrap(fmt.Errorf("could not read directory: %w", err)) + } + + for _, item := range items { + src := filepath.Join(cacheTemp, item.Name()) + dst := filepath.Join(cachePath, item.Name()) + log.DebugContext(ctx, "rename", "src", src, "dst", dst) + if err := os.Rename(src, dst); err != nil { + var linkErr *os.LinkError + if errors.As(err, &linkErr) && errors.Is(linkErr.Err, syscall.EEXIST) { + log.DebugContext(ctx, "cache already exists", "chart", chart.Name, "chart_version", chart.Version, "path", cachePath) + } else { + return errors.Wrap(fmt.Errorf("could not rename: %w", err)) + } + } + } + + log.InfoContext(ctx, "cached", "chart", chart.Name, "chart_version", chart.Version, "path", cachePath) + + return nil +} +func isNotExist(path string) bool { + _, err := os.Stat(path) + return os.IsNotExist(err) +} diff --git a/internal/render/result.go b/internal/render/result.go new file mode 100644 index 00000000..8c5d38d4 --- /dev/null +++ b/internal/render/result.go @@ -0,0 +1,215 @@ +package render + +import ( + "context" + "fmt" + "os" + "path/filepath" + "slices" + + "github.com/holos-run/holos/api/core/v1alpha2" + "github.com/holos-run/holos/internal/errors" + "github.com/holos-run/holos/internal/server/middleware/logger" + "github.com/holos-run/holos/internal/util" +) + +// NewResult returns a new Result with the given holos component. +func NewResult(component v1alpha2.HolosComponent) *Result { + return &Result{ + Kind: "Result", + APIVersion: "v1alpha2", + Component: component, + accumulatedOutput: "", + } +} + +// Result is the build result for display or writing. Holos components Render +// the Result as a data pipeline. +type Result struct { + // Kind is a string value representing the resource this object represents. + Kind string `json:"kind" yaml:"kind" cue:"string | *\"Result\""` + // APIVersion represents the versioned schema of this representation of an object. + APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha2\""` + + // Component represents the common fields of all holos component kinds. + Component v1alpha2.HolosComponent + + // accumulatedOutput accumulates rendered api objects. + accumulatedOutput string +} + +func (r *Result) GetAPIVersion() string { + if r == nil { + return "" + } + return r.APIVersion +} + +func (r *Result) GetKind() string { + if r == nil { + return "" + } + return r.Kind +} + +// Continue returns true if the result should be skipped over. +func (r *Result) Continue() bool { + // Skip over a nil result + if r == nil { + return true + } + return r.Component.Skip +} + +// Name returns the name of the component from the Metadata field. +func (r *Result) Name() string { + if r == nil { + return "" + } + return r.Component.Metadata.Name +} + +// Filename returns the filename representing the rendered api objects of the Result. +func (r *Result) Filename(writeTo string, cluster string) string { + name := r.Name() + return filepath.Join(writeTo, "clusters", cluster, "components", name, name+".gen.yaml") +} + +// KustomizationFilename returns the Flux Kustomization file path. +// +// Deprecated: Use DeployFiles instead. +func (r *Result) KustomizationFilename(writeTo string, cluster string) string { + return filepath.Join(writeTo, "clusters", cluster, "holos", "components", r.Name()+"-kustomization.gen.yaml") +} + +// AccumulatedOutput returns the accumulated rendered output. +func (r *Result) AccumulatedOutput() string { + if r == nil { + return "" + } + return r.accumulatedOutput +} + +// addObjectMap renders the provided APIObjectMap into the accumulated output. +func (r *Result) addObjectMap(ctx context.Context, objectMap v1alpha2.APIObjectMap) { + if r == nil { + return + } + log := logger.FromContext(ctx) + b := []byte(r.AccumulatedOutput()) + kinds := make([]v1alpha2.Kind, 0, len(objectMap)) + // Sort the keys + for kind := range objectMap { + kinds = append(kinds, kind) + } + slices.Sort(kinds) + + for _, kind := range kinds { + v := objectMap[kind] + // Sort the keys + names := make([]v1alpha2.Label, 0, len(v)) + for name := range v { + names = append(names, name) + } + slices.Sort(names) + + for _, name := range names { + yamlString := v[name] + log.Debug(fmt.Sprintf("%s/%s", kind, name), "kind", kind, "name", name) + b = util.EnsureNewline(b) + header := fmt.Sprintf("---\n# Source: CUE apiObjects.%s.%s\n", kind, name) + b = append(b, []byte(header+yamlString)...) + b = util.EnsureNewline(b) + } + } + r.accumulatedOutput = string(b) +} + +// kustomize replaces the accumulated output with the output of kustomize build +func (r *Result) kustomize(ctx context.Context) error { + if r == nil { + return nil + } + log := logger.FromContext(ctx) + if r.Component.Kustomize.ResourcesFile == "" { + log.DebugContext(ctx, "skipping kustomize: no resourcesFile") + return nil + } + if len(r.Component.Kustomize.KustomizeFiles) < 1 { + log.DebugContext(ctx, "skipping kustomize: no kustomizeFiles") + return nil + } + tempDir, err := os.MkdirTemp("", "holos.kustomize") + if err != nil { + return errors.Wrap(err) + } + defer util.Remove(ctx, tempDir) + + // Write the main api object resources file for kustomize. + target := filepath.Join(tempDir, r.Component.Kustomize.ResourcesFile) + b := []byte(r.AccumulatedOutput()) + b = util.EnsureNewline(b) + if err := os.WriteFile(target, b, 0644); err != nil { + return errors.Wrap(fmt.Errorf("could not write resources: %w", err)) + } + log.DebugContext(ctx, "wrote: "+target, "op", "write", "path", target, "bytes", len(b)) + + // Write the kustomization tree, kustomization.yaml must be in this map for kustomize to work. + for file, content := range r.Component.Kustomize.KustomizeFiles { + target := filepath.Join(tempDir, file) + if err := os.MkdirAll(filepath.Dir(target), 0755); err != nil { + return errors.Wrap(err) + } + b := []byte(content) + b = util.EnsureNewline(b) + if err := os.WriteFile(target, b, 0644); err != nil { + return errors.Wrap(fmt.Errorf("could not write: %w", err)) + } + log.DebugContext(ctx, "wrote: "+target, "op", "write", "path", target, "bytes", len(b)) + } + + // Run kustomize. + kOut, err := util.RunCmd(ctx, "kubectl", "kustomize", tempDir) + if err != nil { + log.ErrorContext(ctx, kOut.Stderr.String()) + return errors.Wrap(err) + } + // Replace the accumulated output + r.accumulatedOutput = kOut.Stdout.String() + return nil +} + +func (r *Result) WriteDeployFiles(ctx context.Context, path string) error { + if r == nil { + return nil + } + log := logger.FromContext(ctx) + if len(r.Component.DeployFiles) == 0 { + return nil + } + for k, content := range r.Component.DeployFiles { + path := filepath.Join(path, k) + if err := r.Save(ctx, path, content); err != nil { + return errors.Wrap(err) + } + log.InfoContext(ctx, "wrote deploy file", "path", path, "bytes", len(content)) + } + return nil +} + +// Save writes the content to the filesystem for git ops. +func (r *Result) Save(ctx context.Context, path string, content string) error { + log := logger.FromContext(ctx) + dir := filepath.Dir(path) + if err := os.MkdirAll(dir, os.FileMode(0775)); err != nil { + log.WarnContext(ctx, "could not mkdir", "path", dir, "err", err) + return errors.Wrap(err) + } + // Write the file content + if err := os.WriteFile(path, []byte(content), os.FileMode(0644)); err != nil { + log.WarnContext(ctx, "could not write", "path", path, "err", err) + return errors.Wrap(err) + } + log.DebugContext(ctx, "out: wrote "+path, "action", "write", "path", path, "status", "ok") + return nil +} From caa7560ab9e7f3806bc4acffbe1d8fc8040383cc Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Fri, 28 Jun 2024 16:21:13 -0700 Subject: [PATCH 02/15] (#189) Fix Helm.Chart.namespace: field not allowed Fixes: ``` 4:19PM ERR could not execute version=0.84.1 code=unknown err="could not build /home/jeff/workspace/holos-run/holos-infra/saas2/platform: #Helm.Chart.namespace: field not allowed" loc=platform.go:52 /home/jeff/workspace/holos-run/holos-infra/saas2/buildplan.cue:106:9 /home/jeff/workspace/holos-run/holos-infra/saas2/buildplan.cue:108:3 /home/jeff/workspace/holos-run/holos-infra/saas2/buildplan.cue:118:3 /home/jeff/workspace/holos-run/holos-infra/saas2/buildplan.cue:118:43 /home/jeff/workspace/holos-run/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:48:18 /home/jeff/workspace/holos-run/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:12:13 /home/jeff/workspace/holos-run/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:13:2 ``` --- internal/generate/platforms/holos/buildplan.cue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index 62223842..6846ac67 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -105,7 +105,7 @@ import ( Chart: core.#HelmChart & { metadata: name: string | *Name - namespace: string | *Namespace + metadata: namespace: string | *Namespace chart: name: string | *Name chart: release: chart.name chart: version: string | *Version From e0f439515f69268c87185e8b80d765a4f4801671 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sat, 29 Jun 2024 07:32:57 -0700 Subject: [PATCH 03/15] (#189) Fix holos render platform for v1alpha2 Previously holos render platform failed for the holos platform. The issue was caused by the deployFiles field moving from the BuildPlan down to HolosComponent. This patch fixes the problem by placing the ArgoCD Application resource into a separate Resources entry of the BuildPlan. The sole purpose of this additional entry in the Resources map is to produce the Application resource along side any other components which are part of the build plan. --- .../generate/platforms/holos/buildplan.cue | 54 ++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index 6846ac67..bd77e095 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -104,12 +104,12 @@ import ( Values: {...} Chart: core.#HelmChart & { - metadata: name: string | *Name + metadata: name: string | *Name metadata: namespace: string | *Namespace - chart: name: string | *Name - chart: release: chart.name - chart: version: string | *Version - chart: repository: Repo + chart: name: string | *Name + chart: release: chart.name + chart: version: string | *Version + chart: repository: Repo // Render the values to yaml for holos to provide to helm. valuesContent: yaml.Marshal(Values) @@ -160,7 +160,7 @@ import ( // output represents the build plan provided to the holos cli. Output: #BuildPlan & { - metadata: name: Name + _Name: Name spec: components: helmChartList: [Chart] } } @@ -176,7 +176,7 @@ import ( // output represents the build plan provided to the holos cli. Output: #BuildPlan & { - metadata: name: Name + _Name: Name spec: components: kustomizeBuildList: [Kustomization] } } @@ -191,19 +191,21 @@ import ( // output represents the build plan provided to the holos cli. Output: #BuildPlan & { - metadata: name: Name + _Name: Name // resources is a map unlike other build plans which use a list. spec: components: resources: "\(Name)": { metadata: name: Name - apiObjectMap: (core.#APIObjects & {apiObjects: Resources}).apiObjectMap + apiObjectMap: (#APIObjects & {apiObjects: Resources}).apiObjectMap } } } #BuildPlan: core.#BuildPlan & { - metadata: name: string - // Render the ArgoCD Application - spec: deployFiles: (#Argo & {ComponentName: metadata.name}).deployFiles + _Name: string + spec: components: resources: "\(_Name)": { + // Render the ArgoCD Application + deployFiles: (#Argo & {ComponentName: _Name}).deployFiles + } } // #ArgoApplication represents an argocd Application resource for each @@ -228,3 +230,31 @@ import ( // deployFiles represents the output files to write along side the component. deployFiles: "clusters/\(_ClusterName)/gitops/\(ComponentName).application.gen.yaml": yaml.Marshal(Application) } + +// #APIObjects defines the output format for kubernetes api objects. The holos +// cli expects the yaml representation of each api object in the apiObjectMap +// field. +#APIObjects: { + // apiObjects represents the un-marshalled form of each kubernetes api object + // managed by a holos component. + apiObjects: { + [Kind=string]: { + [string]: { + kind: Kind + ... + } + } + ConfigMap: [string]: corev1.#ConfigMap & {apiVersion: "v1"} + } + + // apiObjectMap holds the marshalled representation of apiObjects + apiObjectMap: { + for kind, v in apiObjects { + "\(kind)": { + for name, obj in v { + "\(name)": yaml.Marshal(obj) + } + } + } + } +} From 313ebc6817769c0f627ea8b3402e96bc2824dced Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sat, 29 Jun 2024 08:04:51 -0700 Subject: [PATCH 04/15] (#189) README --- README.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 00000000..bb46bd82 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# Holos From 4522ee1d4e3d60d9ad0dd07d8b29c4dc1ed2ac56 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sat, 29 Jun 2024 14:54:14 -0700 Subject: [PATCH 05/15] (#189) Working eso-creds-manager with v1alpha2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this patch the eso-creds-manager component renders correctly. This is a `#Kubernetes` type build plan which uses the spec.components.resources map to manage resources. The only issue was needing to provide the namespace to the nested holos component inside the BuildPlan. The ArgoCD Application resource moves to the DeployFiles field of a separate holos component in the same build plan at spec.components.resources.argocd. For this reason a separate Result object is no longer necessary inside of the Holos cli for the purpose of managing Flux or ArgoCD gitops. The CUE code can simply inline whatever gitops resources it wants and the holos cli will write the files relative to the cluster specific deploy directory. Result: ``` ❯ holos render component --cluster-name management components/eso-creds-manager 2:55PM INF result.go:195 wrote deploy file version=0.84.1 path=deploy/clusters/management/gitops/eso-creds-manager.application.gen.yaml bytes=350 2:55PM INF render.go:92 rendered eso-creds-manager version=0.84.1 cluster=management name=eso-creds-manager status=ok action=rendered ``` --- api/core/v1alpha2/buildplan.go | 2 +- api/core/v1alpha2/constants.go | 2 +- internal/builder/builder.go | 6 ------ .../holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue | 2 +- .../holos-run/holos/api/core/v1alpha2/constants_go_gen.cue | 2 +- internal/generate/platforms/holos/buildplan.cue | 3 ++- 6 files changed, 6 insertions(+), 11 deletions(-) diff --git a/api/core/v1alpha2/buildplan.go b/api/core/v1alpha2/buildplan.go index ff8d12f8..f2821648 100644 --- a/api/core/v1alpha2/buildplan.go +++ b/api/core/v1alpha2/buildplan.go @@ -92,7 +92,7 @@ type HolosComponent struct { Kustomize `json:"kustomize,omitempty" yaml:"kustomize,omitempty"` // Skip causes holos to take no action regarding this component. - Skip bool + Skip bool `json:"skip" yaml:"skip" cue:"bool | *false"` } // Metadata represents data about the holos component such as the Name. diff --git a/api/core/v1alpha2/constants.go b/api/core/v1alpha2/constants.go index caa921e7..36ee262b 100644 --- a/api/core/v1alpha2/constants.go +++ b/api/core/v1alpha2/constants.go @@ -1,7 +1,7 @@ package v1alpha2 const ( - APIVersion = "holos.run/v1alpha2" + APIVersion = "v1alpha2" BuildPlanKind = "BuildPlan" HelmChartKind = "HelmChart" // ChartDir is the directory name created in the holos component directory to cache a chart. diff --git a/internal/builder/builder.go b/internal/builder/builder.go index fc642fba..c8e0594d 100644 --- a/internal/builder/builder.go +++ b/internal/builder/builder.go @@ -261,12 +261,6 @@ func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha2.BuildPlan, } } - // Add a separate Result if there are DeployFiles from the BuildPlan. - log.WarnContext(ctx, "TODO: Handle DeployFiles for each holos component.") - // if len(buildPlan.Spec.DeployFiles) > 0 { - // results = append(results, render.NewResult(buildPlan.Metadata.Name)) - // } - log.DebugContext(ctx, "returning results", "len", len(results)) return results, nil diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue index db0c2d6a..d8cbd7b0 100644 --- a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue @@ -70,7 +70,7 @@ package v1alpha2 kustomize?: #Kustomize @go(Kustomize) // Skip causes holos to take no action regarding this component. - Skip: bool + skip: bool & (bool | *false) @go(Skip) } // Metadata represents data about the holos component such as the Name. diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue index d9256bd7..347be330 100644 --- a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/constants_go_gen.cue @@ -4,7 +4,7 @@ package v1alpha2 -#APIVersion: "holos.run/v1alpha2" +#APIVersion: "v1alpha2" #BuildPlanKind: "BuildPlan" #HelmChartKind: "HelmChart" diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index bd77e095..082533f2 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -194,7 +194,8 @@ import ( _Name: Name // resources is a map unlike other build plans which use a list. spec: components: resources: "\(Name)": { - metadata: name: Name + metadata: name: Name + metadata: namespace: Namespace apiObjectMap: (#APIObjects & {apiObjects: Resources}).apiObjectMap } } From accf80200f5351167fa4d3f2a9f910fc97cee04f Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 08:18:58 -0700 Subject: [PATCH 06/15] (#189) Fix pod-identity-webhook Helm chart for v1alpha2 The pod identity webhook component fails to render with v1alpha2. This patch fixes the problem by providing concrete values for enableHooks and the namespace of the helm chart holos component. The namespace is mainly necessary to render the ArgoCD Application resource along side the helm chart output. --- api/core/v1alpha2/helm.go | 2 +- .../holos/api/core/v1alpha2/helm_go_gen.cue | 2 +- internal/generate/platforms/holos/buildplan.cue | 13 +++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/api/core/v1alpha2/helm.go b/api/core/v1alpha2/helm.go index 5928b92b..71d86fb3 100644 --- a/api/core/v1alpha2/helm.go +++ b/api/core/v1alpha2/helm.go @@ -15,7 +15,7 @@ type HelmChart struct { // template` command. ValuesContent string `json:"valuesContent"` // EnableHooks enables helm hooks when executing the `helm template` command. - EnableHooks bool `json:"enableHooks"` + EnableHooks bool `json:"enableHooks" cue:"bool | *false"` } // Chart represents the helm Chart. diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue index d1da1e22..e2afdf72 100644 --- a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue @@ -21,7 +21,7 @@ package v1alpha2 valuesContent: string @go(ValuesContent) // EnableHooks enables helm hooks when executing the `helm template` command. - enableHooks: bool @go(EnableHooks) + enableHooks: bool & (bool | *false) @go(EnableHooks) } // Chart represents the helm Chart. diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index 082533f2..7ea737a2 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -127,7 +127,7 @@ import ( } } - apiObjectMap: (core.#APIObjects & {apiObjects: Resources}).apiObjectMap + apiObjectMap: (#APIObjects & {apiObjects: Resources}).apiObjectMap } // EnableKustomizePostProcessor processes helm output with kustomize if true. @@ -160,7 +160,8 @@ import ( // output represents the build plan provided to the holos cli. Output: #BuildPlan & { - _Name: Name + _Name: Name + _Namespace: Namespace spec: components: helmChartList: [Chart] } } @@ -191,7 +192,8 @@ import ( // output represents the build plan provided to the holos cli. Output: #BuildPlan & { - _Name: Name + _Name: Name + _Namespace: Namespace // resources is a map unlike other build plans which use a list. spec: components: resources: "\(Name)": { metadata: name: Name @@ -202,8 +204,11 @@ import ( } #BuildPlan: core.#BuildPlan & { - _Name: string + _Name: string + _Namespace: string spec: components: resources: "\(_Name)": { + metadata: name: _Name + metadata: namespace: _Namespace // Render the ArgoCD Application deployFiles: (#Argo & {ComponentName: _Name}).deployFiles } From 1fb1798f6011aeef0cea044e8084e0aed8c04cd1 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 09:11:54 -0700 Subject: [PATCH 07/15] (#189) Make HolosComponent Metadata Namespace optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously a metadata.namespace value was required for all holos components. This is a problem because not all resources require a namespace, for example producing the ArgoCD Application resource for each build plan does not need a namespace defined, particularly when managing only CRDs. With this patch we get pretty far: ``` ❯ holos generate platform holos 9:14AM INF platform.go:79 wrote platform.metadata.json version=0.84.1 platform_id=018fa1cf-a609-7463-aa6e-fa53bfded1dc path=/Users/jeff/Holos/holos-infra/saas2/platform.metadata.json 9:14AM INF platform.go:91 generated platform holos version=0.84.1 platform_id=018fa1cf-a609-7463-aa6e-fa53bfded1dc path=/Users/jeff/Holos/holos-infra/saas2 ❯ time holos render platform --concurrency 1 ./platform 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/eso-creds-manager cluster=management num=1 total=73 duration=212.546542ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/cert-letsencrypt cluster=management num=2 total=73 duration=110.363875ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/certificates cluster=management num=3 total=73 duration=154.642541ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/login/zitadel-certs cluster=management num=4 total=73 duration=115.132041ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/ecr-creds-manager cluster=management num=5 total=73 duration=162.559542ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/eks-pod-identity-webhook cluster=management num=6 total=73 duration=135.03ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/crossplane/crds cluster=management num=7 total=73 duration=296.536833ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/crossplane/controller cluster=management num=8 total=73 duration=146.730667ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/backstage/management/certs cluster=management num=9 total=73 duration=117.42625ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/external-secrets cluster=aws1 num=10 total=73 duration=170.574458ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/eso-creds-refresher cluster=aws1 num=11 total=73 duration=161.188625ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/secretstores cluster=aws1 num=12 total=73 duration=153.708458ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/ecr-creds-refresher cluster=aws1 num=13 total=73 duration=130.369166ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/gateway-api cluster=aws1 num=14 total=73 duration=2.078997458s 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/istio/base cluster=aws1 num=15 total=73 duration=145.869084ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/istio/mesh/cni cluster=aws1 num=16 total=73 duration=142.113125ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/istio/mesh/istiod cluster=aws1 num=17 total=73 duration=155.186375ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/istio/mesh/gateway cluster=aws1 num=18 total=73 duration=137.8775ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/istio/mesh/httpbin/backend cluster=aws1 num=19 total=73 duration=116.537458ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/istio/mesh/httpbin/routes cluster=aws1 num=20 total=73 duration=122.709875ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/pgo/crds cluster=aws1 num=21 total=73 duration=271.561666ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/pgo/controller cluster=aws1 num=22 total=73 duration=143.880292ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/login/zitadel-secrets cluster=aws1 num=23 total=73 duration=116.962167ms 9:14AM INF platform.go:52 ok render component version=0.84.1 path=components/login/zitadel-database cluster=aws1 num=24 total=73 duration=121.315875ms 9:14AM ERR could not execute version=0.84.1 code=unknown err="could not build /Users/jeff/Holos/holos-infra/saas2/components/login/zitadel-server: spec.components.helmChartList.0.resourcesFile: field not allowed" loc=builder.go:166 spec.components.helmChartList.0.resourcesFile: field not allowed: /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:106:9 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:106:27 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:118:3 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:122:4 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:125:4 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:125:43 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:162:10 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:165:37 /Users/jeff/Holos/holos-infra/saas2/buildplan.cue:206:13 /Users/jeff/Holos/holos-infra/saas2/components/login/zitadel-server/zitadel.cue:9:1 /Users/jeff/Holos/holos-infra/saas2/components/login/zitadel-server/zitadel.cue:18:9 /Users/jeff/Holos/holos-infra/saas2/components/login/zitadel-server/zitadel.cue:19:9 /Users/jeff/Holos/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:31:8 /Users/jeff/Holos/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:36:15 /Users/jeff/Holos/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:42:19 /Users/jeff/Holos/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:42:22 /Users/jeff/Holos/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:48:18 /Users/jeff/Holos/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:12:13 /Users/jeff/Holos/holos-infra/saas2/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:13:2 9:14AM ERR could not execute version=0.84.1 code=unknown err="could not render component: exit status 1" loc=platform.go:48 holos render platform --concurrency 1 ./platform 6.62s user 1.22s system 133% cpu 5.878 total ``` --- api/core/v1alpha2/buildplan.go | 6 +++++- .../holos/api/core/v1alpha2/buildplan_go_gen.cue | 6 +++++- internal/generate/platforms/holos/buildplan.cue | 11 +++++++---- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/api/core/v1alpha2/buildplan.go b/api/core/v1alpha2/buildplan.go index f2821648..a0425dcf 100644 --- a/api/core/v1alpha2/buildplan.go +++ b/api/core/v1alpha2/buildplan.go @@ -102,7 +102,11 @@ type Metadata struct { // Namespace is the primary namespace of the holos component. A holos // component may manage resources in multiple namespaces, in this case // consider setting the component namespace to default. - Namespace string `json:"namespace" yaml:"namespace"` + // + // This field is optional because not all resources require a namespace, + // particularly CRD's and DeployFiles functionality. + // +optional + Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` } // Kustomize represents resources necessary to execute a kustomize build. diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue index d8cbd7b0..02b00390 100644 --- a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue @@ -81,7 +81,11 @@ package v1alpha2 // Namespace is the primary namespace of the holos component. A holos // component may manage resources in multiple namespaces, in this case // consider setting the component namespace to default. - namespace: string @go(Namespace) + // + // This field is optional because not all resources require a namespace, + // particularly CRD's and DeployFiles functionality. + // +optional + namespace?: string @go(Namespace) } // Kustomize represents resources necessary to execute a kustomize build. diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index 7ea737a2..f0323fd7 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -204,11 +204,14 @@ import ( } #BuildPlan: core.#BuildPlan & { - _Name: string - _Namespace: string + _Name: string + _Namespace?: string spec: components: resources: "\(_Name)": { - metadata: name: _Name - metadata: namespace: _Namespace + metadata: name: _Name + if _Namespace != _|_ { + metadata: namespace: _Namespace + } + // Render the ArgoCD Application deployFiles: (#Argo & {ComponentName: _Name}).deployFiles } From 747ed3462ac7cb9bb50560496446d9152201374c Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 09:23:01 -0700 Subject: [PATCH 08/15] (#189) Fix Helm + Kustomize post renderer for v1alpha2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously the `login/zitadel-server` component failed to render with the following error. This is a result of the kustomize config fileds moving down one level to the `kustomize` field in v1alpha2 relative to `v1alpha`. ``` spec.components.helmChartList.0.kustomizeFiles: field not allowed: ./buildplan.cue:106:9 ./buildplan.cue:106:27 ./buildplan.cue:118:3 ./buildplan.cue:124:4 ./buildplan.cue:125:4 ./buildplan.cue:126:5 ./buildplan.cue:162:10 ./buildplan.cue:165:37 ./buildplan.cue:206:13 ./components/login/zitadel-server/zitadel.cue:9:1 ./components/login/zitadel-server/zitadel.cue:18:9 ./components/login/zitadel-server/zitadel.cue:19:9 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:31:8 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:36:15 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:42:19 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:42:22 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:48:18 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:12:13 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:13:2 spec.components.helmChartList.0.resourcesFile: field not allowed: ./buildplan.cue:106:9 ./buildplan.cue:106:27 ./buildplan.cue:118:3 ./buildplan.cue:122:4 ./buildplan.cue:125:4 ./buildplan.cue:125:43 ./buildplan.cue:162:10 ./buildplan.cue:165:37 ./buildplan.cue:206:13 ./components/login/zitadel-server/zitadel.cue:9:1 ./components/login/zitadel-server/zitadel.cue:18:9 ./components/login/zitadel-server/zitadel.cue:19:9 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:31:8 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:36:15 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:42:19 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:42:22 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue:48:18 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:12:13 ./cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue:13:2 _PlatformConfig: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./buildplan.cue:232:21 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./components/login/login.cue:6:18 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./components/login/zitadel.cue:8:18 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./platform.cue:61:17 ./platform.cue:60:19 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./platform.cue:62:17 ./platform.cue:60:19 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./platform.cue:79:17 ./platform.cue:78:19 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./platform.cue:80:17 ./platform.cue:78:19 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./platform.cue:100:25 ./schema.cue:14:44 _PlatformConfig: invalid interpolation: invalid interpolation: error in call to encoding/json.Unmarshal: json: invalid JSON: ./platform.cue:102:22 ./platform.cue:100:25 ./schema.cue:14:44 _PlatformConfig: error in call to encoding/json.Unmarshal: json: invalid JSON: ./schema.cue:14:44 ``` With this patch the component renders without any further modification: ``` ❯ holos render component --cluster-name aws1 components/login/zitadel-server 9:24AM INF result.go:195 wrote deploy file version=0.84.1 path=deploy/clusters/aws1/gitops/zitadel-server.application.gen.yaml bytes=338 9:24AM INF render.go:92 rendered zitadel-server version=0.84.1 cluster=aws1 name=zitadel-server status=ok action=rendered 9:24AM INF render.go:92 rendered zitadel-server version=0.84.1 cluster=aws1 name=zitadel-server status=ok action=rendered ``` --- internal/generate/platforms/holos/buildplan.cue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index f0323fd7..899b1f0f 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -119,11 +119,11 @@ import ( // resourcesFile represents the file helm output is written two and // kustomize reads from. Typically "resources.yaml" but referenced as a // constant to ensure the holos cli uses the same file. - resourcesFile: core.#ResourcesFile + kustomize: resourcesFile: core.#ResourcesFile // kustomizeFiles represents the files in a kustomize directory tree. - kustomizeFiles: core.#FileContentMap + kustomize: kustomizeFiles: core.#FileContentMap for FileName, Object in KustomizeFiles { - kustomizeFiles: "\(FileName)": yaml.Marshal(Object) + kustomize: kustomizeFiles: "\(FileName)": yaml.Marshal(Object) } } From ef369d48601ce12be0c1fe1244e1055400920a94 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 09:35:25 -0700 Subject: [PATCH 09/15] (#189) Format cue code with `make fmt` Previously the internal cue code was not formatted properly. This patch updates `make fmt` to automatically format the embedded internal platforms. --- Makefile | 1 + docs/examples/cue.mod/module.cue | 1 + .../cloud/mesh/istio/ingress/ingress.cue | 2 +- .../generate/platforms/bare/buildplan.cue | 41 +++--- .../bare/forms/platform/platform-form.cue | 126 +++++++++--------- .../example/forms/platform/platform-form.cue | 126 +++++++++--------- .../generate/platforms/holos/buildplan.cue | 2 +- .../istio/mesh/gateway/gateways.cue | 4 +- .../holos/forms/platform/platform-form.cue | 114 ++++++++-------- 9 files changed, 210 insertions(+), 207 deletions(-) diff --git a/Makefile b/Makefile index 3b49f2b7..2f0445ff 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,7 @@ tidy: ## Tidy go module. .PHONY: fmt fmt: ## Format code. cd docs/examples && cue fmt ./... + cd internal/generate/platforms && cue fmt ./... go fmt ./... .PHONY: vet diff --git a/docs/examples/cue.mod/module.cue b/docs/examples/cue.mod/module.cue index 50f026c0..9a1bc952 100644 --- a/docs/examples/cue.mod/module.cue +++ b/docs/examples/cue.mod/module.cue @@ -1 +1,2 @@ module: "github.com/holos-run/holos/docs/examples" +language: version: "v0.9.2" diff --git a/docs/examples/platforms/reference/clusters/foundation/cloud/mesh/istio/ingress/ingress.cue b/docs/examples/platforms/reference/clusters/foundation/cloud/mesh/istio/ingress/ingress.cue index 7a6c5b1a..e448772b 100644 --- a/docs/examples/platforms/reference/clusters/foundation/cloud/mesh/istio/ingress/ingress.cue +++ b/docs/examples/platforms/reference/clusters/foundation/cloud/mesh/istio/ingress/ingress.cue @@ -151,7 +151,7 @@ let OBJECTS = #APIObjects & { loopback: #Service & { _description: LoopbackDescription metadata: LoopbackMetaName - spec: selector: LoopbackLabels + spec: selector: LoopbackLabels spec: ports: [{port: 80, name: "http"}, {port: 443, name: "https"}] } } diff --git a/internal/generate/platforms/bare/buildplan.cue b/internal/generate/platforms/bare/buildplan.cue index cf30fb2a..a7dde21c 100644 --- a/internal/generate/platforms/bare/buildplan.cue +++ b/internal/generate/platforms/bare/buildplan.cue @@ -1,33 +1,34 @@ package holos import "encoding/yaml" + import v1 "github.com/holos-run/holos/api/v1alpha1" // #Helm represents a holos build plan composed of one or more helm charts. #Helm: { - Name: string - Version: string - Namespace: string + Name: string + Version: string + Namespace: string - Repo: { - name: string | *"" - url: string | *"" - } + Repo: { + name: string | *"" + url: string | *"" + } - Values: {...} + Values: {...} - Chart: v1.#HelmChart & { - metadata: name: string | *Name - namespace: string | *Namespace - chart: name: string | *Name - chart: version: string | *Version - chart: repository: Repo - // Render the values to yaml for holos to provide to helm. - valuesContent: yaml.Marshal(Values) - } + Chart: v1.#HelmChart & { + metadata: name: string | *Name + namespace: string | *Namespace + chart: name: string | *Name + chart: version: string | *Version + chart: repository: Repo + // Render the values to yaml for holos to provide to helm. + valuesContent: yaml.Marshal(Values) + } - // output represents the build plan provided to the holos cli. + // output represents the build plan provided to the holos cli. Output: v1.#BuildPlan & { - spec: components: helmChartList: [Chart] - } + spec: components: helmChartList: [Chart] + } } diff --git a/internal/generate/platforms/bare/forms/platform/platform-form.cue b/internal/generate/platforms/bare/forms/platform/platform-form.cue index 19977a5c..0a54b886 100644 --- a/internal/generate/platforms/bare/forms/platform/platform-form.cue +++ b/internal/generate/platforms/bare/forms/platform/platform-form.cue @@ -58,13 +58,13 @@ let FormBuilder = v1.#FormBuilder & { multiple: true selectAllOption: "Select All" options: [ - {value: "aws", label: "Amazon Web Services"}, - {value: "gcp", label: "Google Cloud Platform"}, - {value: "azure", label: "Microsoft Azure"}, + {value: "aws", label: "Amazon Web Services"}, + {value: "gcp", label: "Google Cloud Platform"}, + {value: "azure", label: "Microsoft Azure"}, {value: "cloudflare", label: "Cloudflare"}, - {value: "github", label: "GitHub"}, - {value: "ois", label: "Open Infrastructure Services"}, - {value: "onprem", label: "On Premises", disabled: true}, + {value: "github", label: "GitHub"}, + {value: "ois", label: "Open Infrastructure Services"}, + {value: "onprem", label: "On Premises", disabled: true}, ] } } @@ -237,73 +237,73 @@ let FormBuilder = v1.#FormBuilder & { } let GCPRegions = [ - {value: "africa-south1", label: "africa-south1"}, - {value: "asia-east1", label: "asia-east1"}, - {value: "asia-east2", label: "asia-east2"}, - {value: "asia-northeast1", label: "asia-northeast1"}, - {value: "asia-northeast2", label: "asia-northeast2"}, - {value: "asia-northeast3", label: "asia-northeast3"}, - {value: "asia-south1", label: "asia-south1"}, - {value: "asia-south2", label: "asia-south2"}, - {value: "asia-southeast1", label: "asia-southeast1"}, - {value: "asia-southeast2", label: "asia-southeast2"}, - {value: "australia-southeast1", label: "australia-southeast1"}, - {value: "australia-southeast2", label: "australia-southeast2"}, - {value: "europe-central2", label: "europe-central2"}, - {value: "europe-north1", label: "europe-north1"}, - {value: "europe-southwest1", label: "europe-southwest1"}, - {value: "europe-west1", label: "europe-west1"}, - {value: "europe-west10", label: "europe-west10"}, - {value: "europe-west12", label: "europe-west12"}, - {value: "europe-west2", label: "europe-west2"}, - {value: "europe-west3", label: "europe-west3"}, - {value: "europe-west4", label: "europe-west4"}, - {value: "europe-west6", label: "europe-west6"}, - {value: "europe-west8", label: "europe-west8"}, - {value: "europe-west9", label: "europe-west9"}, - {value: "me-central1", label: "me-central1"}, - {value: "me-central2", label: "me-central2"}, - {value: "me-west1", label: "me-west1"}, + {value: "africa-south1", label: "africa-south1"}, + {value: "asia-east1", label: "asia-east1"}, + {value: "asia-east2", label: "asia-east2"}, + {value: "asia-northeast1", label: "asia-northeast1"}, + {value: "asia-northeast2", label: "asia-northeast2"}, + {value: "asia-northeast3", label: "asia-northeast3"}, + {value: "asia-south1", label: "asia-south1"}, + {value: "asia-south2", label: "asia-south2"}, + {value: "asia-southeast1", label: "asia-southeast1"}, + {value: "asia-southeast2", label: "asia-southeast2"}, + {value: "australia-southeast1", label: "australia-southeast1"}, + {value: "australia-southeast2", label: "australia-southeast2"}, + {value: "europe-central2", label: "europe-central2"}, + {value: "europe-north1", label: "europe-north1"}, + {value: "europe-southwest1", label: "europe-southwest1"}, + {value: "europe-west1", label: "europe-west1"}, + {value: "europe-west10", label: "europe-west10"}, + {value: "europe-west12", label: "europe-west12"}, + {value: "europe-west2", label: "europe-west2"}, + {value: "europe-west3", label: "europe-west3"}, + {value: "europe-west4", label: "europe-west4"}, + {value: "europe-west6", label: "europe-west6"}, + {value: "europe-west8", label: "europe-west8"}, + {value: "europe-west9", label: "europe-west9"}, + {value: "me-central1", label: "me-central1"}, + {value: "me-central2", label: "me-central2"}, + {value: "me-west1", label: "me-west1"}, {value: "northamerica-northeast1", label: "northamerica-northeast1"}, {value: "northamerica-northeast2", label: "northamerica-northeast2"}, - {value: "southamerica-east1", label: "southamerica-east1"}, - {value: "southamerica-west1", label: "southamerica-west1"}, - {value: "us-central1", label: "us-central1"}, - {value: "us-east1", label: "us-east1"}, - {value: "us-east4", label: "us-east4"}, - {value: "us-east5", label: "us-east5"}, - {value: "us-south1", label: "us-south1"}, - {value: "us-west1", label: "us-west1"}, - {value: "us-west2", label: "us-west2"}, - {value: "us-west3", label: "us-west3"}, - {value: "us-west4", label: "us-west4"}, + {value: "southamerica-east1", label: "southamerica-east1"}, + {value: "southamerica-west1", label: "southamerica-west1"}, + {value: "us-central1", label: "us-central1"}, + {value: "us-east1", label: "us-east1"}, + {value: "us-east4", label: "us-east4"}, + {value: "us-east5", label: "us-east5"}, + {value: "us-south1", label: "us-south1"}, + {value: "us-west1", label: "us-west1"}, + {value: "us-west2", label: "us-west2"}, + {value: "us-west3", label: "us-west3"}, + {value: "us-west4", label: "us-west4"}, ] let AWSRegions = [ - {value: "us-east-1", label: "N. Virginia (us-east-1)"}, - {value: "us-east-2", label: "Ohio (us-east-2)"}, - {value: "us-west-1", label: "N. California (us-west-1)"}, - {value: "us-west-2", label: "Oregon (us-west-2)"}, - {value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"}, - {value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"}, - {value: "ca-central-1", label: "Canada (ca-central-1)"}, - {value: "eu-north-1", label: "Stockholm (eu-north-1)"}, - {value: "eu-west-1", label: "Ireland (eu-west-1)"}, - {value: "eu-west-2", label: "London (eu-west-2)"}, - {value: "eu-west-3", label: "Paris (eu-west-3)"}, - {value: "eu-central-1", label: "Frankfurt (eu-central-1)"}, - {value: "eu-south-1", label: "Milan (eu-south-1)"}, - {value: "af-south-1", label: "Cape Town (af-south-1)"}, + {value: "us-east-1", label: "N. Virginia (us-east-1)"}, + {value: "us-east-2", label: "Ohio (us-east-2)"}, + {value: "us-west-1", label: "N. California (us-west-1)"}, + {value: "us-west-2", label: "Oregon (us-west-2)"}, + {value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"}, + {value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"}, + {value: "ca-central-1", label: "Canada (ca-central-1)"}, + {value: "eu-north-1", label: "Stockholm (eu-north-1)"}, + {value: "eu-west-1", label: "Ireland (eu-west-1)"}, + {value: "eu-west-2", label: "London (eu-west-2)"}, + {value: "eu-west-3", label: "Paris (eu-west-3)"}, + {value: "eu-central-1", label: "Frankfurt (eu-central-1)"}, + {value: "eu-south-1", label: "Milan (eu-south-1)"}, + {value: "af-south-1", label: "Cape Town (af-south-1)"}, {value: "ap-northeast-1", label: "Tokyo (ap-northeast-1)"}, {value: "ap-northeast-2", label: "Seoul (ap-northeast-2)"}, {value: "ap-northeast-3", label: "Osaka (ap-northeast-3)"}, {value: "ap-southeast-1", label: "Singapore (ap-southeast-1)"}, {value: "ap-southeast-2", label: "Sydney (ap-southeast-2)"}, - {value: "ap-east-1", label: "Hong Kong (ap-east-1)"}, - {value: "ap-south-1", label: "Mumbai (ap-south-1)"}, - {value: "me-south-1", label: "Bahrain (me-south-1)"}, - {value: "sa-east-1", label: "São Paulo (sa-east-1)"}, - {value: "cn-north-1", label: "Bejing (cn-north-1)"}, + {value: "ap-east-1", label: "Hong Kong (ap-east-1)"}, + {value: "ap-south-1", label: "Mumbai (ap-south-1)"}, + {value: "me-south-1", label: "Bahrain (me-south-1)"}, + {value: "sa-east-1", label: "São Paulo (sa-east-1)"}, + {value: "cn-north-1", label: "Bejing (cn-north-1)"}, {value: "cn-northwest-1", label: "Ningxia (cn-northwest-1)"}, {value: "ap-southeast-3", label: "Jakarta (ap-southeast-3)"}, ] diff --git a/internal/generate/platforms/example/forms/platform/platform-form.cue b/internal/generate/platforms/example/forms/platform/platform-form.cue index 90d8b51a..d4866645 100644 --- a/internal/generate/platforms/example/forms/platform/platform-form.cue +++ b/internal/generate/platforms/example/forms/platform/platform-form.cue @@ -78,13 +78,13 @@ let FormBuilder = v1.#FormBuilder & { multiple: true selectAllOption: "Select All" options: [ - {value: "aws", label: "Amazon Web Services"}, - {value: "gcp", label: "Google Cloud Platform"}, - {value: "azure", label: "Microsoft Azure"}, + {value: "aws", label: "Amazon Web Services"}, + {value: "gcp", label: "Google Cloud Platform"}, + {value: "azure", label: "Microsoft Azure"}, {value: "cloudflare", label: "Cloudflare"}, - {value: "github", label: "GitHub"}, - {value: "ois", label: "Open Infrastructure Services"}, - {value: "onprem", label: "On Premises", disabled: true}, + {value: "github", label: "GitHub"}, + {value: "ois", label: "Open Infrastructure Services"}, + {value: "onprem", label: "On Premises", disabled: true}, ] } } @@ -281,73 +281,73 @@ let FormBuilder = v1.#FormBuilder & { } let GCPRegions = [ - {value: "africa-south1", label: "africa-south1"}, - {value: "asia-east1", label: "asia-east1"}, - {value: "asia-east2", label: "asia-east2"}, - {value: "asia-northeast1", label: "asia-northeast1"}, - {value: "asia-northeast2", label: "asia-northeast2"}, - {value: "asia-northeast3", label: "asia-northeast3"}, - {value: "asia-south1", label: "asia-south1"}, - {value: "asia-south2", label: "asia-south2"}, - {value: "asia-southeast1", label: "asia-southeast1"}, - {value: "asia-southeast2", label: "asia-southeast2"}, - {value: "australia-southeast1", label: "australia-southeast1"}, - {value: "australia-southeast2", label: "australia-southeast2"}, - {value: "europe-central2", label: "europe-central2"}, - {value: "europe-north1", label: "europe-north1"}, - {value: "europe-southwest1", label: "europe-southwest1"}, - {value: "europe-west1", label: "europe-west1"}, - {value: "europe-west10", label: "europe-west10"}, - {value: "europe-west12", label: "europe-west12"}, - {value: "europe-west2", label: "europe-west2"}, - {value: "europe-west3", label: "europe-west3"}, - {value: "europe-west4", label: "europe-west4"}, - {value: "europe-west6", label: "europe-west6"}, - {value: "europe-west8", label: "europe-west8"}, - {value: "europe-west9", label: "europe-west9"}, - {value: "me-central1", label: "me-central1"}, - {value: "me-central2", label: "me-central2"}, - {value: "me-west1", label: "me-west1"}, + {value: "africa-south1", label: "africa-south1"}, + {value: "asia-east1", label: "asia-east1"}, + {value: "asia-east2", label: "asia-east2"}, + {value: "asia-northeast1", label: "asia-northeast1"}, + {value: "asia-northeast2", label: "asia-northeast2"}, + {value: "asia-northeast3", label: "asia-northeast3"}, + {value: "asia-south1", label: "asia-south1"}, + {value: "asia-south2", label: "asia-south2"}, + {value: "asia-southeast1", label: "asia-southeast1"}, + {value: "asia-southeast2", label: "asia-southeast2"}, + {value: "australia-southeast1", label: "australia-southeast1"}, + {value: "australia-southeast2", label: "australia-southeast2"}, + {value: "europe-central2", label: "europe-central2"}, + {value: "europe-north1", label: "europe-north1"}, + {value: "europe-southwest1", label: "europe-southwest1"}, + {value: "europe-west1", label: "europe-west1"}, + {value: "europe-west10", label: "europe-west10"}, + {value: "europe-west12", label: "europe-west12"}, + {value: "europe-west2", label: "europe-west2"}, + {value: "europe-west3", label: "europe-west3"}, + {value: "europe-west4", label: "europe-west4"}, + {value: "europe-west6", label: "europe-west6"}, + {value: "europe-west8", label: "europe-west8"}, + {value: "europe-west9", label: "europe-west9"}, + {value: "me-central1", label: "me-central1"}, + {value: "me-central2", label: "me-central2"}, + {value: "me-west1", label: "me-west1"}, {value: "northamerica-northeast1", label: "northamerica-northeast1"}, {value: "northamerica-northeast2", label: "northamerica-northeast2"}, - {value: "southamerica-east1", label: "southamerica-east1"}, - {value: "southamerica-west1", label: "southamerica-west1"}, - {value: "us-central1", label: "us-central1"}, - {value: "us-east1", label: "us-east1"}, - {value: "us-east4", label: "us-east4"}, - {value: "us-east5", label: "us-east5"}, - {value: "us-south1", label: "us-south1"}, - {value: "us-west1", label: "us-west1"}, - {value: "us-west2", label: "us-west2"}, - {value: "us-west3", label: "us-west3"}, - {value: "us-west4", label: "us-west4"}, + {value: "southamerica-east1", label: "southamerica-east1"}, + {value: "southamerica-west1", label: "southamerica-west1"}, + {value: "us-central1", label: "us-central1"}, + {value: "us-east1", label: "us-east1"}, + {value: "us-east4", label: "us-east4"}, + {value: "us-east5", label: "us-east5"}, + {value: "us-south1", label: "us-south1"}, + {value: "us-west1", label: "us-west1"}, + {value: "us-west2", label: "us-west2"}, + {value: "us-west3", label: "us-west3"}, + {value: "us-west4", label: "us-west4"}, ] let AWSRegions = [ - {value: "us-east-1", label: "N. Virginia (us-east-1)"}, - {value: "us-east-2", label: "Ohio (us-east-2)"}, - {value: "us-west-1", label: "N. California (us-west-1)"}, - {value: "us-west-2", label: "Oregon (us-west-2)"}, - {value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"}, - {value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"}, - {value: "ca-central-1", label: "Canada (ca-central-1)"}, - {value: "eu-north-1", label: "Stockholm (eu-north-1)"}, - {value: "eu-west-1", label: "Ireland (eu-west-1)"}, - {value: "eu-west-2", label: "London (eu-west-2)"}, - {value: "eu-west-3", label: "Paris (eu-west-3)"}, - {value: "eu-central-1", label: "Frankfurt (eu-central-1)"}, - {value: "eu-south-1", label: "Milan (eu-south-1)"}, - {value: "af-south-1", label: "Cape Town (af-south-1)"}, + {value: "us-east-1", label: "N. Virginia (us-east-1)"}, + {value: "us-east-2", label: "Ohio (us-east-2)"}, + {value: "us-west-1", label: "N. California (us-west-1)"}, + {value: "us-west-2", label: "Oregon (us-west-2)"}, + {value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"}, + {value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"}, + {value: "ca-central-1", label: "Canada (ca-central-1)"}, + {value: "eu-north-1", label: "Stockholm (eu-north-1)"}, + {value: "eu-west-1", label: "Ireland (eu-west-1)"}, + {value: "eu-west-2", label: "London (eu-west-2)"}, + {value: "eu-west-3", label: "Paris (eu-west-3)"}, + {value: "eu-central-1", label: "Frankfurt (eu-central-1)"}, + {value: "eu-south-1", label: "Milan (eu-south-1)"}, + {value: "af-south-1", label: "Cape Town (af-south-1)"}, {value: "ap-northeast-1", label: "Tokyo (ap-northeast-1)"}, {value: "ap-northeast-2", label: "Seoul (ap-northeast-2)"}, {value: "ap-northeast-3", label: "Osaka (ap-northeast-3)"}, {value: "ap-southeast-1", label: "Singapore (ap-southeast-1)"}, {value: "ap-southeast-2", label: "Sydney (ap-southeast-2)"}, - {value: "ap-east-1", label: "Hong Kong (ap-east-1)"}, - {value: "ap-south-1", label: "Mumbai (ap-south-1)"}, - {value: "me-south-1", label: "Bahrain (me-south-1)"}, - {value: "sa-east-1", label: "São Paulo (sa-east-1)"}, - {value: "cn-north-1", label: "Bejing (cn-north-1)"}, + {value: "ap-east-1", label: "Hong Kong (ap-east-1)"}, + {value: "ap-south-1", label: "Mumbai (ap-south-1)"}, + {value: "me-south-1", label: "Bahrain (me-south-1)"}, + {value: "sa-east-1", label: "São Paulo (sa-east-1)"}, + {value: "cn-north-1", label: "Bejing (cn-north-1)"}, {value: "cn-northwest-1", label: "Ningxia (cn-northwest-1)"}, {value: "ap-southeast-3", label: "Jakarta (ap-southeast-3)"}, ] diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index 899b1f0f..1fbfabf1 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -206,7 +206,7 @@ import ( #BuildPlan: core.#BuildPlan & { _Name: string _Namespace?: string - spec: components: resources: "\(_Name)": { + spec: components: resources: (_Name): { metadata: name: _Name if _Namespace != _|_ { metadata: namespace: _Namespace diff --git a/internal/generate/platforms/holos/components/istio/mesh/gateway/gateways.cue b/internal/generate/platforms/holos/components/istio/mesh/gateway/gateways.cue index 3cfa0ae6..255f01ca 100644 --- a/internal/generate/platforms/holos/components/istio/mesh/gateway/gateways.cue +++ b/internal/generate/platforms/holos/components/istio/mesh/gateway/gateways.cue @@ -44,8 +44,8 @@ let Objects = { // Work with a struct of listeners instead of a list. _listeners: (#WildcardListener & {Name: "admin", Selector: _Selector.GrantSubdomainAdmin, Cluster: true}).Output _listeners: (#WildcardListener & {Name: "login", Selector: _Selector.GrantSubdomainLogin, Cluster: false}).Output - _listeners: (#WildcardListener & {Name: "app", Selector: _Selector.GrantSubdomainApp, Cluster: false}).Output - _listeners: (#WildcardListener & {Name: "app", Selector: _Selector.GrantSubdomainApp, Cluster: true}).Output + _listeners: (#WildcardListener & {Name: "app", Selector: _Selector.GrantSubdomainApp, Cluster: false}).Output + _listeners: (#WildcardListener & {Name: "app", Selector: _Selector.GrantSubdomainApp, Cluster: true}).Output listeners: [for x in _listeners {x}] } } diff --git a/internal/generate/platforms/holos/forms/platform/platform-form.cue b/internal/generate/platforms/holos/forms/platform/platform-form.cue index 6aa07098..75f83237 100644 --- a/internal/generate/platforms/holos/forms/platform/platform-form.cue +++ b/internal/generate/platforms/holos/forms/platform/platform-form.cue @@ -385,73 +385,73 @@ let FormBuilder = v1.#FormBuilder & { } let GCPRegions = [ - {value: "africa-south1", label: "africa-south1"}, - {value: "asia-east1", label: "asia-east1"}, - {value: "asia-east2", label: "asia-east2"}, - {value: "asia-northeast1", label: "asia-northeast1"}, - {value: "asia-northeast2", label: "asia-northeast2"}, - {value: "asia-northeast3", label: "asia-northeast3"}, - {value: "asia-south1", label: "asia-south1"}, - {value: "asia-south2", label: "asia-south2"}, - {value: "asia-southeast1", label: "asia-southeast1"}, - {value: "asia-southeast2", label: "asia-southeast2"}, - {value: "australia-southeast1", label: "australia-southeast1"}, - {value: "australia-southeast2", label: "australia-southeast2"}, - {value: "europe-central2", label: "europe-central2"}, - {value: "europe-north1", label: "europe-north1"}, - {value: "europe-southwest1", label: "europe-southwest1"}, - {value: "europe-west1", label: "europe-west1"}, - {value: "europe-west10", label: "europe-west10"}, - {value: "europe-west12", label: "europe-west12"}, - {value: "europe-west2", label: "europe-west2"}, - {value: "europe-west3", label: "europe-west3"}, - {value: "europe-west4", label: "europe-west4"}, - {value: "europe-west6", label: "europe-west6"}, - {value: "europe-west8", label: "europe-west8"}, - {value: "europe-west9", label: "europe-west9"}, - {value: "me-central1", label: "me-central1"}, - {value: "me-central2", label: "me-central2"}, - {value: "me-west1", label: "me-west1"}, + {value: "africa-south1", label: "africa-south1"}, + {value: "asia-east1", label: "asia-east1"}, + {value: "asia-east2", label: "asia-east2"}, + {value: "asia-northeast1", label: "asia-northeast1"}, + {value: "asia-northeast2", label: "asia-northeast2"}, + {value: "asia-northeast3", label: "asia-northeast3"}, + {value: "asia-south1", label: "asia-south1"}, + {value: "asia-south2", label: "asia-south2"}, + {value: "asia-southeast1", label: "asia-southeast1"}, + {value: "asia-southeast2", label: "asia-southeast2"}, + {value: "australia-southeast1", label: "australia-southeast1"}, + {value: "australia-southeast2", label: "australia-southeast2"}, + {value: "europe-central2", label: "europe-central2"}, + {value: "europe-north1", label: "europe-north1"}, + {value: "europe-southwest1", label: "europe-southwest1"}, + {value: "europe-west1", label: "europe-west1"}, + {value: "europe-west10", label: "europe-west10"}, + {value: "europe-west12", label: "europe-west12"}, + {value: "europe-west2", label: "europe-west2"}, + {value: "europe-west3", label: "europe-west3"}, + {value: "europe-west4", label: "europe-west4"}, + {value: "europe-west6", label: "europe-west6"}, + {value: "europe-west8", label: "europe-west8"}, + {value: "europe-west9", label: "europe-west9"}, + {value: "me-central1", label: "me-central1"}, + {value: "me-central2", label: "me-central2"}, + {value: "me-west1", label: "me-west1"}, {value: "northamerica-northeast1", label: "northamerica-northeast1"}, {value: "northamerica-northeast2", label: "northamerica-northeast2"}, - {value: "southamerica-east1", label: "southamerica-east1"}, - {value: "southamerica-west1", label: "southamerica-west1"}, - {value: "us-central1", label: "us-central1"}, - {value: "us-east1", label: "us-east1"}, - {value: "us-east4", label: "us-east4"}, - {value: "us-east5", label: "us-east5"}, - {value: "us-south1", label: "us-south1"}, - {value: "us-west1", label: "us-west1"}, - {value: "us-west2", label: "us-west2"}, - {value: "us-west3", label: "us-west3"}, - {value: "us-west4", label: "us-west4"}, + {value: "southamerica-east1", label: "southamerica-east1"}, + {value: "southamerica-west1", label: "southamerica-west1"}, + {value: "us-central1", label: "us-central1"}, + {value: "us-east1", label: "us-east1"}, + {value: "us-east4", label: "us-east4"}, + {value: "us-east5", label: "us-east5"}, + {value: "us-south1", label: "us-south1"}, + {value: "us-west1", label: "us-west1"}, + {value: "us-west2", label: "us-west2"}, + {value: "us-west3", label: "us-west3"}, + {value: "us-west4", label: "us-west4"}, ] let AWSRegions = [ - {value: "us-east-1", label: "N. Virginia (us-east-1)"}, - {value: "us-east-2", label: "Ohio (us-east-2)"}, - {value: "us-west-1", label: "N. California (us-west-1)"}, - {value: "us-west-2", label: "Oregon (us-west-2)"}, - {value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"}, - {value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"}, - {value: "ca-central-1", label: "Canada (ca-central-1)"}, - {value: "eu-north-1", label: "Stockholm (eu-north-1)"}, - {value: "eu-west-1", label: "Ireland (eu-west-1)"}, - {value: "eu-west-2", label: "London (eu-west-2)"}, - {value: "eu-west-3", label: "Paris (eu-west-3)"}, - {value: "eu-central-1", label: "Frankfurt (eu-central-1)"}, - {value: "eu-south-1", label: "Milan (eu-south-1)"}, - {value: "af-south-1", label: "Cape Town (af-south-1)"}, + {value: "us-east-1", label: "N. Virginia (us-east-1)"}, + {value: "us-east-2", label: "Ohio (us-east-2)"}, + {value: "us-west-1", label: "N. California (us-west-1)"}, + {value: "us-west-2", label: "Oregon (us-west-2)"}, + {value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"}, + {value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"}, + {value: "ca-central-1", label: "Canada (ca-central-1)"}, + {value: "eu-north-1", label: "Stockholm (eu-north-1)"}, + {value: "eu-west-1", label: "Ireland (eu-west-1)"}, + {value: "eu-west-2", label: "London (eu-west-2)"}, + {value: "eu-west-3", label: "Paris (eu-west-3)"}, + {value: "eu-central-1", label: "Frankfurt (eu-central-1)"}, + {value: "eu-south-1", label: "Milan (eu-south-1)"}, + {value: "af-south-1", label: "Cape Town (af-south-1)"}, {value: "ap-northeast-1", label: "Tokyo (ap-northeast-1)"}, {value: "ap-northeast-2", label: "Seoul (ap-northeast-2)"}, {value: "ap-northeast-3", label: "Osaka (ap-northeast-3)"}, {value: "ap-southeast-1", label: "Singapore (ap-southeast-1)"}, {value: "ap-southeast-2", label: "Sydney (ap-southeast-2)"}, - {value: "ap-east-1", label: "Hong Kong (ap-east-1)"}, - {value: "ap-south-1", label: "Mumbai (ap-south-1)"}, - {value: "me-south-1", label: "Bahrain (me-south-1)"}, - {value: "sa-east-1", label: "São Paulo (sa-east-1)"}, - {value: "cn-north-1", label: "Bejing (cn-north-1)"}, + {value: "ap-east-1", label: "Hong Kong (ap-east-1)"}, + {value: "ap-south-1", label: "Mumbai (ap-south-1)"}, + {value: "me-south-1", label: "Bahrain (me-south-1)"}, + {value: "sa-east-1", label: "São Paulo (sa-east-1)"}, + {value: "cn-north-1", label: "Bejing (cn-north-1)"}, {value: "cn-northwest-1", label: "Ningxia (cn-northwest-1)"}, {value: "ap-southeast-3", label: "Jakarta (ap-southeast-3)"}, ] From 42509a34cf50ef92e429a7ce2e32331ba2fae8a3 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 09:37:14 -0700 Subject: [PATCH 10/15] (#189) Fix the gitops Application component name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously components appeared to be duplicated, it was not clear to the user one build plan results in two components: one for the k8s yaml and one for the gitops argocd Application resource. ``` ❯ holos render component --cluster-name aws1 components/login/zitadel-server 9:27AM INF result.go:195 wrote deploy file version=0.84.1 path=deploy/clusters/aws1/gitops/zitadel-server.application.gen.yaml bytes=338 9:27AM INF render.go:92 rendered zitadel-server version=0.84.1 cluster=aws1 name=zitadel-server status=ok action=rendered 9:27AM INF render.go:92 rendered zitadel-server version=0.84.1 cluster=aws1 name=zitadel-server status=ok action=rendered ``` This patch prefixes the ArgoCD Application resource, which is implemented as a separate HolosComponent in the same BuildPlan. The result is more clear about what is going on: ``` ❯ holos render component --cluster-name aws1 components/login/zitadel-server 9:39AM INF result.go:195 wrote deploy file version=0.84.1 path=deploy/clusters/aws1/gitops/zitadel-server.application.gen.yaml bytes=338 9:39AM INF render.go:92 rendered gitops/zitadel-server version=0.84.1 cluster=aws1 name=gitops/zitadel-server status=ok action=rendered 9:39AM INF render.go:92 rendered zitadel-server version=0.84.1 cluster=aws1 name=zitadel-server status=ok action=rendered ``` --- internal/generate/platforms/holos/buildplan.cue | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index 1fbfabf1..bd37f8a7 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -206,13 +206,15 @@ import ( #BuildPlan: core.#BuildPlan & { _Name: string _Namespace?: string - spec: components: resources: (_Name): { - metadata: name: _Name + let NAME = "gitops/\(_Name)" + + // Render the ArgoCD Application for GitOps. + spec: components: resources: (NAME): { + metadata: name: NAME if _Namespace != _|_ { metadata: namespace: _Namespace } - // Render the ArgoCD Application deployFiles: (#Argo & {ComponentName: _Name}).deployFiles } } From faa46c54d8b6acf751a000e217facdd140f37fc1 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 10:12:34 -0700 Subject: [PATCH 11/15] (#189) Do not write empty files with gitops results Previosly, the holos component Results for each ArgoCD Application resource managed as part of each BuildPlan results in an empty file being written for the empty list of k8s api objects. This patch fixes the problem by skipping writing the accumulated output of API objects with the Result metadata.name starts with `gitops/`. This is kind of a hack, but it works well enough for now. --- internal/cli/render/render.go | 15 ++++++++------- internal/render/result.go | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/internal/cli/render/render.go b/internal/cli/render/render.go index 31c87a4c..f3cce84a 100644 --- a/internal/cli/render/render.go +++ b/internal/cli/render/render.go @@ -78,15 +78,15 @@ func NewComponent(cfg *holos.Config) *cobra.Command { if err := result.WriteDeployFiles(ctx, cfg.WriteTo()); err != nil { return errors.Wrap(err) } - // Build plans don't have anything but DeployFiles to write. - if result.GetKind() == "BuildPlan" { - continue - } // API Objects - path := result.Filename(cfg.WriteTo(), cfg.ClusterName()) - if err := result.Save(ctx, path, result.AccumulatedOutput()); err != nil { - return errors.Wrap(err) + if result.SkipWriteAccumulatedOutput() { + log.DebugContext(ctx, "skipped writing k8s objects for "+result.Name()) + } else { + path := result.Filename(cfg.WriteTo(), cfg.ClusterName()) + if err := result.Save(ctx, path, result.AccumulatedOutput()); err != nil { + return errors.Wrap(err) + } } log.InfoContext(ctx, "rendered "+result.Name(), "status", "ok", "action", "rendered") @@ -130,6 +130,7 @@ type Result interface { KustomizationFilename(writeTo string, cluster string) string Save(ctx context.Context, path string, content string) error AccumulatedOutput() string + SkipWriteAccumulatedOutput() bool WriteDeployFiles(ctx context.Context, writeTo string) error GetKind() string GetAPIVersion() string diff --git a/internal/render/result.go b/internal/render/result.go index 8c5d38d4..2ea198fc 100644 --- a/internal/render/result.go +++ b/internal/render/result.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "slices" + "strings" "github.com/holos-run/holos/api/core/v1alpha2" "github.com/holos-run/holos/internal/errors" @@ -213,3 +214,17 @@ func (r *Result) Save(ctx context.Context, path string, content string) error { log.DebugContext(ctx, "out: wrote "+path, "action", "write", "path", path, "status", "ok") return nil } + +// SkipWriteAccumulatedOutput returns true if writing the accumulated output of +// k8s api objects should be skipped. Useful for results which only write +// deployment files, like Flux or ArgoCD GitOps resources. +func (r *Result) SkipWriteAccumulatedOutput() bool { + if r == nil { + return true + } + // This is a hack and should be moved to a HolosComponent field or similar. + if strings.HasPrefix(r.Component.Metadata.Name, "gitops/") { + return true + } + return false +} From 33970dafe8a0615addbe61b12624504d7405d744 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 10:27:48 -0700 Subject: [PATCH 12/15] (#189) Version 0.85.0 v1alpha2 --- version/embedded/minor | 2 +- version/embedded/patch | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/version/embedded/minor b/version/embedded/minor index 871727de..a862eb84 100644 --- a/version/embedded/minor +++ b/version/embedded/minor @@ -1 +1 @@ -84 +85 diff --git a/version/embedded/patch b/version/embedded/patch index d00491fd..573541ac 100644 --- a/version/embedded/patch +++ b/version/embedded/patch @@ -1 +1 @@ -1 +0 From 1d81b3c3b44349c2108b6b5471a148bc3638e143 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 14:32:19 -0700 Subject: [PATCH 13/15] (#189) Clarify documentation of v1alpha2 Focusing on the purpose of #APIObjects --- Makefile | 1 + api/core/v1alpha2/apiobjects.go | 42 ++++++++++ api/core/v1alpha2/buildplan.go | 82 +++++++++---------- api/core/v1alpha2/helm.go | 13 +-- .../api/core/v1alpha2/apiobjects_go_gen.cue | 47 +++++++++++ .../api/core/v1alpha2/buildplan_go_gen.cue | 40 ++++----- .../holos/api/core/v1alpha2/helm_go_gen.cue | 9 +- .../generate/platforms/holos/buildplan.cue | 2 +- 8 files changed, 157 insertions(+), 79 deletions(-) create mode 100644 api/core/v1alpha2/apiobjects.go create mode 100644 internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/apiobjects_go_gen.cue diff --git a/Makefile b/Makefile index 2f0445ff..1c979174 100644 --- a/Makefile +++ b/Makefile @@ -132,6 +132,7 @@ go-deps: ## tool versions pinned in tools.go go install google.golang.org/protobuf/cmd/protoc-gen-go go install connectrpc.com/connect/cmd/protoc-gen-connect-go go install honnef.co/go/tools/cmd/staticcheck + go install golang.org/x/tools/cmd/godoc # curl https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash .PHONY: frontend-deps diff --git a/api/core/v1alpha2/apiobjects.go b/api/core/v1alpha2/apiobjects.go new file mode 100644 index 00000000..33bcb4cb --- /dev/null +++ b/api/core/v1alpha2/apiobjects.go @@ -0,0 +1,42 @@ +package v1alpha2 + +import "google.golang.org/protobuf/types/known/structpb" + +// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking. +type Label string + +// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking. +type Kind string + +// APIObjectMap represents the marshalled yaml representation of kubernetes api +// objects. Do not produce an APIObjectMap directly, instead use [APIObjects] +// to produce the marshalled yaml representation from CUE data. +// +// Example: +// +// # CUE +// apiObjectMap: (#APIObjects & {apiObjects: Resources}).apiObjectMap +type APIObjectMap map[Kind]map[Label]string + +// APIObjects represents kubernetes api objects to apply to the api server. +// Useful to mix in resources to each HolosComponent type, for example adding an +// ExternalSecret to a HelmChart HolosComponent. +// +// Kind must be the resource kind, e.g. Deployment or Service. +// +// Label is an arbitrary internal identifier to uniquely identify the resource +// within the context of a `holos` command. Holos will never write the +// intermediate label to rendered output. +// +// Refer to [HolosComponent] which accepts an [APIObjectMap] field provided by +// [APIObjects]. +type APIObjects struct { + // APIObjects represents Kubernetes API objects defined directly from CUE + // code. Useful to mix in resources, for example adding an ExternalSecret + // resource to a HelmChart HolosComponent. + APIObjects map[Kind]map[Label]structpb.Struct `json:"apiObjects"` + // APIObjectMap represents the marshalled yaml representation of APIObjects, + // useful to inspect the rendered representation of the resource which will be + // sent to the kubernetes API server. + APIObjectMap APIObjectMap `json:"apiObjectMap"` +} diff --git a/api/core/v1alpha2/buildplan.go b/api/core/v1alpha2/buildplan.go index a0425dcf..d7589789 100644 --- a/api/core/v1alpha2/buildplan.go +++ b/api/core/v1alpha2/buildplan.go @@ -5,29 +5,21 @@ import ( "strings" ) -// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking. -type Label string - -// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking. -type Kind string - -// APIObjectMap is the shape of marshalled api objects returned from cue to the -// holos cli. A map is used to improve the clarity of error messages from cue -// relative to a list. -type APIObjectMap map[Kind]map[Label]string - // FileContentMap represents a mapping of file names to file content. type FileContentMap map[string]string // BuildPlan represents a build plan for the holos cli to execute. A build plan -// is a set of zero or more holos components. +// is a set of zero or more holos components. The purpose of a BuildPlan is to +// define one or more [HolosComponent] kinds, for example a [HelmChart] or +// [KustomizeBuild]. +// +// A BuildPlan usually has an additional empty [KubernetesObjects] for the +// purpose of using the [HolosComponent] DeployFiles field to deploy an ArgoCD +// or Flux gitops resource for the holos component. type BuildPlan struct { - // Kind is a string value representing the resource this object represents. - Kind string `json:"kind" yaml:"kind" cue:"\"BuildPlan\""` - // APIVersion represents the versioned schema of this representation of an object. - APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha2\""` - // Spec represents the specification. - Spec BuildPlanSpec `json:"spec" yaml:"spec"` + Kind string `json:"kind" cue:"\"BuildPlan\""` + APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""` + Spec BuildPlanSpec `json:"spec"` } func (bp *BuildPlan) Validate() error { @@ -48,57 +40,57 @@ func (bp *BuildPlan) ResultCapacity() (count int) { if bp == nil { return 0 } - count = len(bp.Spec.Components.HelmChartList) - // +len(bp.Spec.Components.KubernetesObjectsList) - // +len(bp.Spec.Components.KustomizeBuildList) - // +len(bp.Spec.Components.Resources) + count = len(bp.Spec.Components.HelmChartList) + + len(bp.Spec.Components.KubernetesObjectsList) + + len(bp.Spec.Components.KustomizeBuildList) + + len(bp.Spec.Components.Resources) return count } type BuildPlanSpec struct { - Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"` - Components BuildPlanComponents `json:"components,omitempty" yaml:"components,omitempty"` + Disabled bool `json:"disabled,omitempty"` + Components BuildPlanComponents `json:"components,omitempty"` } type BuildPlanComponents struct { - Resources map[string]KubernetesObjects `json:"resources,omitempty" yaml:"resources,omitempty"` - KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty" yaml:"kubernetesObjectsList,omitempty"` - HelmChartList []HelmChart `json:"helmChartList,omitempty" yaml:"helmChartList,omitempty"` - KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty" yaml:"kustomizeBuildList,omitempty"` + Resources map[string]KubernetesObjects `json:"resources,omitempty"` + KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty"` + HelmChartList []HelmChart `json:"helmChartList,omitempty"` + KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty"` } // HolosComponent defines the fields common to all holos component kinds. Every // holos component kind should embed HolosComponent. type HolosComponent struct { // Kind is a string value representing the resource this object represents. - Kind string `json:"kind" yaml:"kind"` + Kind string `json:"kind"` // APIVersion represents the versioned schema of this representation of an object. - APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha2\""` + APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""` // Metadata represents data about the holos component such as the Name. - Metadata Metadata `json:"metadata" yaml:"metadata"` + Metadata Metadata `json:"metadata"` - // APIObjectMap holds the marshalled representation of api objects. Think of - // these objects as being mixed into the upstream resources, for example - // adding an ExternalSecret to a rendered Helm chart. - APIObjectMap APIObjectMap `json:"apiObjectMap,omitempty" yaml:"apiObjectMap,omitempty"` + // APIObjectMap holds the marshalled representation of api objects. Useful to + // mix in resources to each HolosComponent type, for example adding an + // ExternalSecret to a HelmChart HolosComponent. Refer to [APIObjects]. + APIObjectMap APIObjectMap `json:"apiObjectMap,omitempty"` // DeployFiles represents file paths relative to the cluster deploy directory // with the value representing the file content. Intended for defining the // ArgoCD Application resource or Flux Kustomization resource from within CUE, // but may be used to render any file related to the build plan from CUE. - DeployFiles FileContentMap `json:"deployFiles,omitempty" yaml:"deployFiles,omitempty"` + DeployFiles FileContentMap `json:"deployFiles,omitempty"` // Kustomize represents a kubectl kustomize build post-processing step. - Kustomize `json:"kustomize,omitempty" yaml:"kustomize,omitempty"` + Kustomize `json:"kustomize,omitempty"` // Skip causes holos to take no action regarding this component. - Skip bool `json:"skip" yaml:"skip" cue:"bool | *false"` + Skip bool `json:"skip" cue:"bool | *false"` } // Metadata represents data about the holos component such as the Name. type Metadata struct { // Name represents the name of the holos component. - Name string `json:"name" yaml:"name"` + Name string `json:"name"` // Namespace is the primary namespace of the holos component. A holos // component may manage resources in multiple namespaces, in this case // consider setting the component namespace to default. @@ -106,17 +98,19 @@ type Metadata struct { // This field is optional because not all resources require a namespace, // particularly CRD's and DeployFiles functionality. // +optional - Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Namespace string `json:"namespace,omitempty"` } // Kustomize represents resources necessary to execute a kustomize build. // Intended for at least two use cases: // -// 1. Process raw yaml file resources in a holos component directory. -// 2. Post process a HelmChart to inject istio, add custom labels, etc... +// 1. Process a [KustomizeBuild] [HolosComponent] which represents raw yaml +// file resources in a holos component directory. +// 2. Post process a [HelmChart] [HolosComponent] to inject istio, patch jobs, +// add custom labels, etc... type Kustomize struct { // KustomizeFiles holds file contents for kustomize, e.g. patch files. - KustomizeFiles FileContentMap `json:"kustomizeFiles,omitempty" yaml:"kustomizeFiles,omitempty"` + KustomizeFiles FileContentMap `json:"kustomizeFiles,omitempty"` // ResourcesFile is the file name used for api objects in kustomization.yaml - ResourcesFile string `json:"resourcesFile,omitempty" yaml:"resourcesFile,omitempty"` + ResourcesFile string `json:"resourcesFile,omitempty"` } diff --git a/api/core/v1alpha2/helm.go b/api/core/v1alpha2/helm.go index 71d86fb3..caa652fe 100644 --- a/api/core/v1alpha2/helm.go +++ b/api/core/v1alpha2/helm.go @@ -3,13 +3,14 @@ package v1alpha2 // HelmChart represents a holos component which wraps around an upstream helm // chart. Holos orchestrates helm by providing values obtained from CUE, // renders the output using `helm template`, then post-processes the helm output -// yaml using the general functionality provided by HolosComponent, for example -// kustomize post-rendering and mixing in additional kubernetes api objects. +// yaml using the general functionality provided by [HolosComponent], for +// example [Kustomize] post-rendering and mixing in additional kubernetes api +// objects. type HelmChart struct { - HolosComponent `json:",inline" yaml:",inline"` - Kind string `json:"kind" yaml:"kind" cue:"\"HelmChart\""` + HolosComponent `json:",inline"` + Kind string `json:"kind" cue:"\"HelmChart\""` - // Chart represents the helm Chart. + // Chart represents a helm chart to manage. Chart Chart `json:"chart"` // ValuesContent represents the values.yaml file holos passes to the `helm // template` command. @@ -18,7 +19,7 @@ type HelmChart struct { EnableHooks bool `json:"enableHooks" cue:"bool | *false"` } -// Chart represents the helm Chart. +// Chart represents a helm chart. type Chart struct { // Name represents the chart name. Name string `json:"name"` diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/apiobjects_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/apiobjects_go_gen.cue new file mode 100644 index 00000000..6605374e --- /dev/null +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/apiobjects_go_gen.cue @@ -0,0 +1,47 @@ +// Code generated by cue get go. DO NOT EDIT. + +//cue:generate cue get go github.com/holos-run/holos/api/core/v1alpha2 + +package v1alpha2 + +import "google.golang.org/protobuf/types/known/structpb" + +// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking. +#Label: string + +// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking. +#Kind: string + +// APIObjectMap represents the marshalled yaml representation of kubernetes api +// objects. Do not produce an APIObjectMap directly, instead use [APIObjects] +// to produce the marshalled yaml representation from CUE data. +// +// Example: +// +// # CUE +// apiObjectMap: (#APIObjects & {apiObjects: Resources}).apiObjectMap +#APIObjectMap: {[string]: [string]: string} + +// APIObjects represents kubernetes api objects to apply to the api server. +// Useful to mix in resources to each HolosComponent type, for example adding an +// ExternalSecret to a HelmChart HolosComponent. +// +// Kind must be the resource kind, e.g. Deployment or Service. +// +// Label is an arbitrary internal identifier to uniquely identify the resource +// within the context of a `holos` command. Holos will never write the +// intermediate label to rendered output. +// +// Refer to [HolosComponent] which accepts an [APIObjectMap] field provided by +// [APIObjects]. +#APIObjects: { + // APIObjects represents Kubernetes API objects defined directly from CUE + // code. Useful to mix in resources, for example adding an ExternalSecret + // resource to a HelmChart HolosComponent. + apiObjects: {[string]: [string]: structpb.#Struct} @go(APIObjects,map[Kind]map[Label]structpb.Struct) + + // APIObjectMap represents the marshalled yaml representation of APIObjects, + // useful to inspect the rendered representation of the resource which will be + // sent to the kubernetes API server. + apiObjectMap: #APIObjectMap @go(APIObjectMap) +} diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue index 02b00390..c70aba00 100644 --- a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/buildplan_go_gen.cue @@ -4,31 +4,21 @@ package v1alpha2 -// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking. -#Label: string - -// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking. -#Kind: string - -// APIObjectMap is the shape of marshalled api objects returned from cue to the -// holos cli. A map is used to improve the clarity of error messages from cue -// relative to a list. -#APIObjectMap: {[string]: [string]: string} - // FileContentMap represents a mapping of file names to file content. #FileContentMap: {[string]: string} // BuildPlan represents a build plan for the holos cli to execute. A build plan -// is a set of zero or more holos components. +// is a set of zero or more holos components. The purpose of a BuildPlan is to +// define one or more [HolosComponent] kinds, for example a [HelmChart] or +// [KustomizeBuild]. +// +// A BuildPlan usually has an additional empty [KubernetesObjects] for the +// purpose of using the [HolosComponent] DeployFiles field to deploy an ArgoCD +// or Flux gitops resource for the holos component. #BuildPlan: { - // Kind is a string value representing the resource this object represents. - kind: string & "BuildPlan" @go(Kind) - - // APIVersion represents the versioned schema of this representation of an object. + kind: string & "BuildPlan" @go(Kind) apiVersion: string & (string | *"v1alpha2") @go(APIVersion) - - // Spec represents the specification. - spec: #BuildPlanSpec @go(Spec) + spec: #BuildPlanSpec @go(Spec) } #BuildPlanSpec: { @@ -55,9 +45,9 @@ package v1alpha2 // Metadata represents data about the holos component such as the Name. metadata: #Metadata @go(Metadata) - // APIObjectMap holds the marshalled representation of api objects. Think of - // these objects as being mixed into the upstream resources, for example - // adding an ExternalSecret to a rendered Helm chart. + // APIObjectMap holds the marshalled representation of api objects. Useful to + // mix in resources to each HolosComponent type, for example adding an + // ExternalSecret to a HelmChart HolosComponent. Refer to [APIObjects]. apiObjectMap?: #APIObjectMap @go(APIObjectMap) // DeployFiles represents file paths relative to the cluster deploy directory @@ -91,8 +81,10 @@ package v1alpha2 // Kustomize represents resources necessary to execute a kustomize build. // Intended for at least two use cases: // -// 1. Process raw yaml file resources in a holos component directory. -// 2. Post process a HelmChart to inject istio, add custom labels, etc... +// 1. Process a [KustomizeBuild] [HolosComponent] which represents raw yaml +// file resources in a holos component directory. +// 2. Post process a [HelmChart] [HolosComponent] to inject istio, patch jobs, +// add custom labels, etc... #Kustomize: { // KustomizeFiles holds file contents for kustomize, e.g. patch files. kustomizeFiles?: #FileContentMap @go(KustomizeFiles) diff --git a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue index e2afdf72..5aa54c76 100644 --- a/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue +++ b/internal/generate/platforms/cue.mod/gen/github.com/holos-run/holos/api/core/v1alpha2/helm_go_gen.cue @@ -7,13 +7,14 @@ package v1alpha2 // HelmChart represents a holos component which wraps around an upstream helm // chart. Holos orchestrates helm by providing values obtained from CUE, // renders the output using `helm template`, then post-processes the helm output -// yaml using the general functionality provided by HolosComponent, for example -// kustomize post-rendering and mixing in additional kubernetes api objects. +// yaml using the general functionality provided by [HolosComponent], for +// example [Kustomize] post-rendering and mixing in additional kubernetes api +// objects. #HelmChart: { #HolosComponent kind: string & "HelmChart" @go(Kind) - // Chart represents the helm Chart. + // Chart represents a helm chart to manage. chart: #Chart @go(Chart) // ValuesContent represents the values.yaml file holos passes to the `helm @@ -24,7 +25,7 @@ package v1alpha2 enableHooks: bool & (bool | *false) @go(EnableHooks) } -// Chart represents the helm Chart. +// Chart represents a helm chart. #Chart: { // Name represents the chart name. name: string @go(Name) diff --git a/internal/generate/platforms/holos/buildplan.cue b/internal/generate/platforms/holos/buildplan.cue index bd37f8a7..542bba59 100644 --- a/internal/generate/platforms/holos/buildplan.cue +++ b/internal/generate/platforms/holos/buildplan.cue @@ -245,7 +245,7 @@ import ( // #APIObjects defines the output format for kubernetes api objects. The holos // cli expects the yaml representation of each api object in the apiObjectMap // field. -#APIObjects: { +#APIObjects: core.#APIObjects & { // apiObjects represents the un-marshalled form of each kubernetes api object // managed by a holos component. apiObjects: { From 51706507604834b368aaf2d91027ac8e94b97532 Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 14:50:50 -0700 Subject: [PATCH 14/15] (#189) Remove yaml tags for v1alpha2. Unnecessary, json tags are sufficient for both yaml and json. --- api/core/v1alpha2/apiobjects.go | 8 ++------ api/core/v1alpha2/core.go | 24 +++++++++++++----------- api/core/v1alpha2/kubernetesobjects.go | 8 ++++---- api/core/v1alpha2/kustomizebuild.go | 4 ++-- api/meta/v1alpha2/meta.go | 8 ++++---- 5 files changed, 25 insertions(+), 27 deletions(-) diff --git a/api/core/v1alpha2/apiobjects.go b/api/core/v1alpha2/apiobjects.go index 33bcb4cb..ba270c8a 100644 --- a/api/core/v1alpha2/apiobjects.go +++ b/api/core/v1alpha2/apiobjects.go @@ -10,12 +10,8 @@ type Kind string // APIObjectMap represents the marshalled yaml representation of kubernetes api // objects. Do not produce an APIObjectMap directly, instead use [APIObjects] -// to produce the marshalled yaml representation from CUE data. -// -// Example: -// -// # CUE -// apiObjectMap: (#APIObjects & {apiObjects: Resources}).apiObjectMap +// to produce the marshalled yaml representation from CUE data, then provide the +// result to [HolosComponent]. type APIObjectMap map[Kind]map[Label]string // APIObjects represents kubernetes api objects to apply to the api server. diff --git a/api/core/v1alpha2/core.go b/api/core/v1alpha2/core.go index 77efcbd4..2c7174c0 100644 --- a/api/core/v1alpha2/core.go +++ b/api/core/v1alpha2/core.go @@ -7,6 +7,11 @@ package v1alpha2 import "google.golang.org/protobuf/types/known/structpb" +type PlatformMetadata struct { + // Name represents the Platform name. + Name string `json:"name"` +} + // Platform represents a platform to manage. A Platform resource informs holos // which components to build. The platform resource also acts as a container // for the platform model form values provided by the PlatformService. The @@ -14,17 +19,14 @@ import "google.golang.org/protobuf/types/known/structpb" // model, and holos components to build into one resource. type Platform struct { // Kind is a string value representing the resource this object represents. - Kind string `json:"kind" yaml:"kind" cue:"\"Platform\""` + Kind string `json:"kind" cue:"\"Platform\""` // APIVersion represents the versioned schema of this representation of an object. - APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha2\""` + APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""` // Metadata represents data about the object such as the Name. - Metadata struct { - // Name represents the Platform name. - Name string `json:"name" yaml:"name"` - } `json:"metadata" yaml:"metadata"` + Metadata PlatformMetadata `json:"metadata"` // Spec represents the specification. - Spec PlatformSpec `json:"spec" yaml:"spec"` + Spec PlatformSpec `json:"spec"` } // PlatformSpec represents the specification of a Platform. Think of a platform @@ -33,15 +35,15 @@ type Platform struct { type PlatformSpec struct { // Model represents the platform model holos gets from from the // PlatformService.GetPlatform rpc method and provides to CUE using a tag. - Model structpb.Struct `json:"model" yaml:"model"` + Model structpb.Struct `json:"model"` // Components represents a list of holos components to manage. - Components []PlatformSpecComponent `json:"components" yaml:"components"` + Components []PlatformSpecComponent `json:"components"` } // PlatformSpecComponent represents a holos component to build or render. type PlatformSpecComponent struct { // Path is the path of the component relative to the platform root. - Path string `json:"path" yaml:"path"` + Path string `json:"path"` // Cluster is the cluster name to provide when rendering the component. - Cluster string `json:"cluster" yaml:"cluster"` + Cluster string `json:"cluster"` } diff --git a/api/core/v1alpha2/kubernetesobjects.go b/api/core/v1alpha2/kubernetesobjects.go index 2cacce00..8471a190 100644 --- a/api/core/v1alpha2/kubernetesobjects.go +++ b/api/core/v1alpha2/kubernetesobjects.go @@ -2,9 +2,9 @@ package v1alpha2 const KubernetesObjectsKind = "KubernetesObjects" -// KubernetesObjects represents a holos component composed of kubernetes api -// objects provided directly from CUE. +// KubernetesObjects represents a [HolosComponent] composed of kubernetes api +// objects provided directly from CUE using [APIObjects]. type KubernetesObjects struct { - HolosComponent `json:",inline" yaml:",inline"` - Kind string `json:"kind" yaml:"kind" cue:"\"KubernetesObjects\""` + HolosComponent `json:",inline"` + Kind string `json:"kind" cue:"\"KubernetesObjects\""` } diff --git a/api/core/v1alpha2/kustomizebuild.go b/api/core/v1alpha2/kustomizebuild.go index ad9a376b..945ecf14 100644 --- a/api/core/v1alpha2/kustomizebuild.go +++ b/api/core/v1alpha2/kustomizebuild.go @@ -3,6 +3,6 @@ package v1alpha2 // KustomizeBuild renders plain yaml files in the holos component directory // using kubectl kustomize build. type KustomizeBuild struct { - HolosComponent `json:",inline" yaml:",inline"` - Kind string `json:"kind" yaml:"kind" cue:"\"KustomizeBuild\""` + HolosComponent `json:",inline"` + Kind string `json:"kind" cue:"\"KustomizeBuild\""` } diff --git a/api/meta/v1alpha2/meta.go b/api/meta/v1alpha2/meta.go index 759d41de..b771c138 100644 --- a/api/meta/v1alpha2/meta.go +++ b/api/meta/v1alpha2/meta.go @@ -5,9 +5,9 @@ package v1alpha2 // Structures that are versioned or persisted should inline TypeMeta. type TypeMeta struct { // Kind is a string value representing the resource this object represents. - Kind string `json:"kind" yaml:"kind"` + Kind string `json:"kind"` // APIVersion defines the versioned schema of this representation of an object. - APIVersion string `json:"apiVersion" yaml:"apiVersion" cue:"string | *\"v1alpha2\""` + APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""` } func (tm *TypeMeta) GetKind() string { @@ -31,7 +31,7 @@ type Discriminator interface { // kubernetes api objects. type ObjectMeta struct { // Name uniquely identifies the holos component instance and must be suitable as a file name. - Name string `json:"name,omitempty" yaml:"name,omitempty"` + Name string `json:"name,omitempty"` // Namespace confines a holos component to a single namespace via kustomize if set. - Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"` + Namespace string `json:"namespace,omitempty"` } From a39807a8580f8690028aa65ea782b2456b86847f Mon Sep 17 00:00:00 2001 From: Jeff McCune Date: Sun, 30 Jun 2024 15:04:39 -0700 Subject: [PATCH 15/15] (#189) go mod tidy --- go.mod | 5 ++--- go.sum | 40 ++-------------------------------------- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/go.mod b/go.mod index 3d3581da..3268d6ca 100644 --- a/go.mod +++ b/go.mod @@ -30,6 +30,7 @@ require ( github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.26.0 + golang.org/x/sync v0.7.0 golang.org/x/tools v0.22.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002 @@ -44,7 +45,6 @@ require ( require ( ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43 // indirect - cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.3.0 // indirect cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2 // indirect github.com/AlecAivazis/survey/v2 v2.3.7 // indirect @@ -215,6 +215,7 @@ require ( github.com/stoewer/go-strcase v1.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tchap/go-patricia/v2 v2.3.1 // indirect + github.com/tetratelabs/wazero v1.6.0 // indirect github.com/tidwall/gjson v1.17.1 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect @@ -242,12 +243,10 @@ require ( golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/oauth2 v0.20.0 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/term v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect google.golang.org/grpc v1.62.1 // indirect diff --git a/go.sum b/go.sum index c9c9daa7..aaaa7bb1 100644 --- a/go.sum +++ b/go.sum @@ -23,10 +23,6 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= @@ -48,12 +44,8 @@ connectrpc.com/otelconnect v0.7.0 h1:ZH55ZZtcJOTKWWLy3qmL4Pam4RzRWBJFOqTPyAqCXkY connectrpc.com/otelconnect v0.7.0/go.mod h1:Bt2ivBymHZHqxvo4HkJ0EwHuUzQN6k2l0oH+mp/8nwc= connectrpc.com/validate v0.1.0 h1:r55jirxMK7HO/xZwVHj3w2XkVFarsUM77ZDy367NtH4= connectrpc.com/validate v0.1.0/go.mod h1:GU47c9/x/gd+u9wRSPkrQOP46gx2rMN+Wo37EHgI3Ow= -cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e h1:GwCVItFUPxwdsEYnlUcJ6PJxOjTeFFCKOh6QWg4oAzQ= -cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24= cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2 h1:BnG6pr9TTr6CYlrJznYUDj6V7xldD1W+1iXPum0wT/w= cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2/go.mod h1:pK23AUVXuNzzTpfMCA06sxZGeVQ/75FdVtW249de9Uo= -cuelang.org/go v0.8.0 h1:fO1XPe/SUGtc7dhnGnTPbpIDoQm/XxhDtoSF7jzO01c= -cuelang.org/go v0.8.0/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI= cuelang.org/go v0.9.2 h1:pfNiry2PdRBr02G/aKm5k2vhzmqbAOoaB4WurmEbWvs= cuelang.org/go v0.9.2/go.mod h1:qpAYsLOf7gTM1YdEg6cxh553uZ4q9ZDWlPbtZr9q1Wk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -644,6 +636,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g= +github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= @@ -726,10 +720,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -769,8 +759,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -806,10 +794,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -818,8 +802,6 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= -golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -887,10 +869,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -899,10 +877,6 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -912,14 +886,10 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -973,10 +943,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1005,8 +971,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=