Compare commits

...

7 Commits

Author SHA1 Message Date
Jeff McCune
73f777759e quickstart: mix-in argocd application resource
Users need to customize the default behavior of the core components,
like the Helm schema wrapper to mix-in an ArgoCD Application resource to
each component.  This patch wires up #Helm in the holos package to
schema.#Helm from the v1alpha3 api.

The result is illustrated in the Quickstart documentation, it is now
simple for users to modify the definition of a Helm component such that
Application resources are mixed in to every component in the platform.
2024-09-09 14:09:24 -07:00
Jeff McCune
8b9070f185 api: add schema to platform cue.mod for consistency
Previosly the end user needed to write, or at least copy and paste, a
large amount of boiler plate code to achieve the goal of declaring a
helm chart component.  There is a gap between the cue code:

    (#Helm & Chart).Output

And the full BuildPlan produced for the Holos cli to execute the
rendering process.  The boiler plate code in schema.cue at the root of
the platform infrastructure repository was largely responsible for
defining how a BuildPlan with one HelmChart component is derived from
this #Helm definition.

This patch moves the definitions into a new, documented API named
`schema`.  End users are expected to define their own #Helm definition
using the schema.#Helm, like so in the root level schema.cue:

    #Helm: schema.#Helm
2024-09-09 11:22:36 -07:00
Jeff McCune
1e8861c8b7 builder: relax api version requirement to fix deploy-dev
Without this patch deployments to the dev environment are failing with
the following error when commits are pushed to the main branch.

    GIT_DETAIL=v0.93.0-3-g4db3fb4 GIT_SUFFIX= bash ./hack/deploy-dev
    Cloning into 'holos-infra'...
    could not validate
    could not run: could not validate invalid BuildPlan: apiVersion invalid: want: v1alpha3 have: v1alpha2 at internal/builder/builder.go:308
    could not run: could not render component: exit status 1 at internal/render/platform.go:48
    make: *** [Makefile:147: dev-deploy] Error 1

This patch removes the api version check in the build plan validation
function.  In the future, we should pass an interface internally in the
holos executable.

The result is holos render platform ./platform succeeds with this patch
applied.
2024-09-06 20:58:56 -07:00
Jeff McCune
bdc182f4eb quickstart: generate podinfo helm chart 2024-09-06 20:57:35 -07:00
Jeff McCune
4db3fb4ead api: optional platform.spec.model
Previously the CUE code needed to specify the Platform.spec.model field,
which created friction.  This patch adds a cue struct tag to unify the
field with an open struct.

    ❯ holos render platform ./platform --log-level=debug
    could not run: could not marshal cue instance platform: cue: marshal error: spec.model: cannot convert incomplete value "_" to JSON at internal/builder/platform.go:45
    spec.model: cannot convert incomplete value "_" to JSON

The render command completes successfully with this patch without the
user having to provide a value for the spec.model field.
2024-09-06 13:38:48 -07:00
Jeff McCune
1911c7fe01 generate: add bare bones quickstart platform
This patch adds the minimal amount of CUE code necessary to successfully
run the following two commands from the quickstart.

    holos generate platform quickstart
    holos render platform ./platform

The result is no componets are rendered, so nothing is done, but it does
succeeed.

This patch surfaces some friction and inconsistency with how the Model
is passed in and the initial structure of the _PlatformConfig.  The tags
are required otherwise holos errors out.
2024-09-06 12:16:59 -07:00
Jeff McCune
5e582ec5c6 generate: do not require registration when generating a platform
Without this patch the `holos generate platform` command automatically
makes an rpc call to holos server.  This creates friction for the
quickstart guide because we don't need to require users to register and
have an organization and platform already created in the server just to
generate a simple platform to exercise a simple helm chart component.

A future patch should implement the behavior of linking a server side
platform to a local git repository by making the API call to get the
platform ID then updating the platform.metadata.json file.
2024-09-06 11:27:05 -07:00
22 changed files with 819 additions and 47 deletions

View File

@@ -30,6 +30,7 @@
"errgroup",
"fieldmaskpb",
"flushcache",
"gendoc",
"ghaction",
"gitops",
"godoc",
@@ -68,6 +69,7 @@
"pflag",
"PKCE",
"platformconnect",
"podinfo",
"promhttp",
"protobuf",
"protojson",

View File

@@ -2,11 +2,6 @@ package v1alpha3
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
@@ -24,13 +19,18 @@ type Platform struct {
Spec PlatformSpec `json:"spec"`
}
type PlatformMetadata struct {
// Name represents the Platform name.
Name string `json:"name"`
}
// PlatformSpec represents the specification of a Platform. Think of a platform
// specification as a list of platform components to apply to a list of
// kubernetes clusters combined with the user-specified Platform Model.
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"`
Model structpb.Struct `json:"model" cue:"{...}"`
// Components represents a list of holos components to manage.
Components []PlatformSpecComponent `json:"components"`
}

View File

@@ -0,0 +1,99 @@
// Package v1alpha3 contains CUE definitions intended as convenience wrappers
// around the core data types defined in package core. The purpose of these
// wrappers is to make life easier for platform engineers by reducing boiler
// plate code and generating component build plans in a consistent manner.
package v1alpha3
import core "github.com/holos-run/holos/api/core/v1alpha3"
//go:generate ../../../hack/gendoc
// Helm provides a BuildPlan via the Output field which contains one HelmChart
// from package core. Useful as a convenience wrapper to render a HelmChart
// with optional mix-in resources and Kustomization post-processing.
type Helm struct {
// Name represents the chart name.
Name string
// Version represents the chart version.
Version string
// Namespace represents the helm namespace option when rendering the chart.
Namespace string
// Resources are kubernetes api objects to mix into the output.
Resources map[string]any `cue:"{...}"`
// Repo represents the chart repository
Repo struct {
Name string `json:"name"`
URL string `json:"url"`
}
// Values represents data to marshal into a values.yaml for helm.
Values interface{} `cue:"{...}"`
// Chart represents the derived HelmChart for inclusion in the BuildPlan
// Output field value. The default HelmChart field values are derived from
// other Helm field values and should be sufficient for most use cases.
Chart core.HelmChart
// EnableKustomizePostProcessor processes helm output with kustomize if true.
EnableKustomizePostProcessor bool `cue:"true | *false"`
// KustomizeFiles represents additional files to include in a Kustomization
// resources list. Useful to patch helm output. The implementation is a
// struct with filename keys and structs as values. Holos encodes the struct
// value to yaml then writes the result to the filename key. Component
// authors may then reference the filename in the kustomization.yaml resources
// or patches lists.
// Requires EnableKustomizePostProcessor: true.
KustomizeFiles map[string]any `cue:"{[string]: {...}}"`
// KustomizePatches represents patches to apply to the helm output. Requires
// EnableKustomizePostProcessor: true.
KustomizePatches map[core.InternalLabel]any `cue:"{[string]: {...}}"`
// KustomizeResources represents additional resources files to include in the
// kustomize resources list.
KustomizeResources map[string]any `cue:"{[string]: {...}}"`
// ArgoConfig represents the ArgoCD GitOps configuration for this Component.
ArgoConfig ArgoConfig
// Output represents the derived BuildPlan for the Holos cli to render.
Output core.BuildPlan
}
// Resources represents the default schema for a Kubernetes API object resource.
// For example, a Service, Namespace or Deployment. The top level key is the
// kind of resource so default behavior and strict schema enforcement may be
// enforced for the kind. The second level keys are an arbitrary internal
// label, which serves as the default value for the resource metadata name
// field, but may differ for situations where the same resource kind and name
// are managed in different namespaces.
//
// Refer to [definitions.cue] for the CUE schema definition as an example to
// build on when defining your own Components.
//
// [definitions.cue]: https://github.com/holos-run/holos/blob/main/internal/generate/platforms/cue.mod/pkg/github.com/holos-run/holos/api/schema/v1alpha3/definitions.cue#L9
// type Resources map[string]map[string]any
// ArgoConfig represents the ArgoCD GitOps configuration for a Component.
// Useful to define once at the root of the Platform configuration and reuse
// across all Components.
type ArgoConfig struct {
// Enabled causes holos to render an ArgoCD Application resource for GitOps if true.
Enabled bool `cue:"true | *false"`
// ClusterName represents the cluster within the platform the Application
// resource is intended for.
ClusterName string
// DeployRoot represents the path from the git repository root to the `deploy`
// rendering output directory. Used as a prefix for the
// Application.spec.source.path field.
DeployRoot string `cue:"string | *\".\""`
// RepoURL represents the value passed to the Application.spec.source.repoURL
// field.
RepoURL string
// TargetRevision represents the value passed to the
// Application.spec.source.targetRevision field. Defaults to the branch named
// main.
TargetRevision string `cue:"string | *\"main\""`
}

View File

@@ -368,7 +368,7 @@ PlatformSpec represents the specification of a Platform. Think of a platform spe
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"`
Model structpb.Struct `json:"model" cue:"{...}"`
// Components represents a list of holos components to manage.
Components []PlatformSpecComponent `json:"components"`
}

View File

@@ -0,0 +1,101 @@
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
# v1alpha3
```go
import "github.com/holos-run/holos/api/schema/v1alpha3"
```
Package v1alpha3 contains CUE definitions intended as convenience wrappers around the core data types defined in package core. The purpose of these wrappers is to make life easier for platform engineers by reducing boiler plate code and generating component build plans in a consistent manner.
## Index
- [type ArgoConfig](<#ArgoConfig>)
- [type Helm](<#Helm>)
<a name="ArgoConfig"></a>
## type ArgoConfig {#ArgoConfig}
ArgoConfig represents the ArgoCD GitOps configuration for a Component. Useful to define once at the root of the Platform configuration and reuse across all Components.
```go
type ArgoConfig struct {
// Enabled causes holos to render an ArgoCD Application resource for GitOps if true.
Enabled bool `cue:"true | *false"`
// ClusterName represents the cluster within the platform the Application
// resource is intended for.
ClusterName string
// DeployRoot represents the path from the git repository root to the `deploy`
// rendering output directory. Used as a prefix for the
// Application.spec.source.path field.
DeployRoot string `cue:"string | *\".\""`
// RepoURL represents the value passed to the Application.spec.source.repoURL
// field.
RepoURL string
// TargetRevision represents the value passed to the
// Application.spec.source.targetRevision field. Defaults to the branch named
// main.
TargetRevision string `cue:"string | *\"main\""`
}
```
<a name="Helm"></a>
## type Helm {#Helm}
Helm provides a BuildPlan via the Output field which contains one HelmChart from package core. Useful as a convenience wrapper to render a HelmChart with optional mix\-in resources and Kustomization post\-processing.
```go
type Helm struct {
// Name represents the chart name.
Name string
// Version represents the chart version.
Version string
// Namespace represents the helm namespace option when rendering the chart.
Namespace string
// Resources are kubernetes api objects to mix into the output.
Resources map[string]any `cue:"{...}"`
// Repo represents the chart repository
Repo struct {
Name string `json:"name"`
URL string `json:"url"`
}
// Values represents data to marshal into a values.yaml for helm.
Values interface{} `cue:"{...}"`
// Chart represents the derived HelmChart for inclusion in the BuildPlan
// Output field value. The default HelmChart field values are derived from
// other Helm field values and should be sufficient for most use cases.
Chart core.HelmChart
// EnableKustomizePostProcessor processes helm output with kustomize if true.
EnableKustomizePostProcessor bool `cue:"true | *false"`
// KustomizeFiles represents additional files to include in a Kustomization
// resources list. Useful to patch helm output. The implementation is a
// struct with filename keys and structs as values. Holos encodes the struct
// value to yaml then writes the result to the filename key. Component
// authors may then reference the filename in the kustomization.yaml resources
// or patches lists.
// Requires EnableKustomizePostProcessor: true.
KustomizeFiles map[string]any `cue:"{[string]: {...}}"`
// KustomizePatches represents patches to apply to the helm output. Requires
// EnableKustomizePostProcessor: true.
KustomizePatches map[core.InternalLabel]any `cue:"{[string]: {...}}"`
// KustomizeResources represents additional resources files to include in the
// kustomize resources list.
KustomizeResources map[string]any `cue:"{[string]: {...}}"`
// ArgoConfig represents the ArgoCD GitOps configuration for this Component.
ArgoConfig ArgoConfig
// Output represents the derived BuildPlan for the Holos cli to render.
Output core.BuildPlan
}
```
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)

View File

@@ -67,3 +67,245 @@ Commit the generated platform config to the repository.
git add .
git commit -m "holos generate platform quickstart - $(holos --version)"
```
## Create a Component
The platform you generated is empty. Rendering the platform succeeds, but does
nothing because there are no Components listed in the Platform spec:
```cue
package holos
import core "github.com/holos-run/holos/api/core/v1alpha3"
core.#Platform & {
metadata: name: "quickstart"
}
```
<Tabs groupId="render">
<TabItem value="command" label="Command">
```bash
holos render platform ./platform
```
</TabItem>
<TabItem value="output" label="Output">
```bash
# No output is produced, the Platform contains no Components.
```
</TabItem>
</Tabs>
Generate the CUE code definition for a Component that wraps the podinfo Helm
chart.
<Tabs groupId="gen-podinfo">
<TabItem value="command" label="Command">
```bash
mkdir -p components
(cd components && holos generate component helm podinfo)
```
</TabItem>
<TabItem value="output" label="Output">
```txt
generated component
```
</TabItem>
</Tabs>
The HelmChart Component is defined in the `components/podinfo/podinfo.cue` file, for example:
```cue
package holos
// Produce a helm chart build plan.
(#Helm & Chart).Output
let Chart = {
Name: "podinfo"
Version: "6.6.2"
Namespace: "default"
Repo: name: "podinfo"
Repo: url: "https://stefanprodan.github.io/podinfo"
Values: {}
}
```
In this example we're providing the minimal information about the Helm chart we
want to manage in this Component. The name, version, Kubernetes namespace to
deploy into, and the chart repository location.
This chart deploys cleanly with no values provided, but we include an empty
struct to illustrate how Holos improves the consistency and safety of Helm
values by taking advantage the strong type checking in CUE. Shared values, such
as the organization domain name, can safely be passed to all Components across
all clusters in the Platform.
## Render the Component
Individual components can be rendered without needing to be included in a
Platform spec, useful when developing a new component.
<Tabs groupId="render-podinfo">
<TabItem value="command" label="Command">
```bash
holos render component ./components/podinfo --cluster-name=default
```
</TabItem>
<TabItem value="output" label="Output">
```txt
cached
rendered podinfo
```
</TabItem>
</Tabs>
First, the command caches the helm chart locally to speed up subsequent
renderings. Then the command executes helm to produce the output which is
written into the deploy directory.
<Tabs groupId="tree-podinfo">
<TabItem value="command" label="Command">
```bash
tree deploy
```
</TabItem>
<TabItem value="output" label="Output">
```txt
deploy
└── clusters
└── default
└── components
└── podinfo
└── podinfo.gen.yaml
```
</TabItem>
</Tabs>
The component is deployed to one cluster named default. The same component is
often deployed to multiple clusters, for example east and west for reliability.
This example is equivalent to executing `helm template` on the chart.
## Mix in an ArgoCD Application
So far we've seen how Holos is a convenient wrapper around Helm, but we haven't
yet seen how it makes it easier to consistently and safely manage all of the
software that goes into a platform. We'll mix in an ArgoCD
[Application][application] resource to manage the podinfo Component with GitOps.
We'll define this configuration in a way that is automatically and consistently
re-used across all Components added to the Platform in the future, including
Components which are not Helm charts.
Create a new file named `argocd.cue` in the root of your git repository with the
following contents:
<Tabs groupId="argocd-config">
<TabItem value="command" label="File: argocd.cue">
```cue
package holos
#ArgoConfig: {
Enabled: true
RepoURL: "https://example.com/holos-quickstart.git"
}
```
</TabItem>
<TabItem value="note" label="Note">
If you plan to apply the rendered output to a real cluster, change the RepoURL
to the url of the git repository you created in this guide. It is sufficient to
keep the example URL if you're getting a feel for Holos and inspecting the
rendered output without applying it to a live cluster.
</TabItem>
</Tabs>
With this file in place, render the component again.
<Tabs groupId="render-podinfo-argocd">
<TabItem value="command" label="Command">
```bash
holos render component ./components/podinfo --cluster-name=default
```
</TabItem>
<TabItem value="output" label="Output">
```txt
wrote deploy file
rendered gitops/podinfo
rendered podinfo
```
</TabItem>
</Tabs>
The render command uses the cached chart this time around. Then, the helm
template output is rendered along with an additional ArgoCD Application resource
for GitOps in the `podinfo.application.gen.yaml` file.
<Tabs groupId="tree-podinfo-argocd">
<TabItem value="command" label="Command">
```bash
tree deploy
```
</TabItem>
<TabItem value="output" label="Output">
```txt
deploy
└── clusters
└── default
├── components
│   └── podinfo
│   └── podinfo.gen.yaml
└── gitops
└── podinfo.application.gen.yaml
```
</TabItem>
</Tabs>
The Application resource looks like:
<Tabs groupId="podinfo-application">
<TabItem value="file" label="podinfo.application.gen.yaml">
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: podinfo
namespace: argocd
spec:
destination:
server: https://kubernetes.default.svc
project: default
source:
path: ./deploy/clusters/default/components/podinfo
repoURL: https://example.com/holos-quickstart.git
targetRevision: main
```
</TabItem>
</Tabs>
:::tip
Holos will generate a similar Application resource for all additional Components
added to your Platform.
:::
In this section we learned how Holos provides a simple way to add an ArgoCD
Application resource for the podinfo Component. Holos further provides
consistency by creating a similar Application resource for every subsequent
Component added to the platform in the future, all by defining the configuration
of ArgoCD in a single place.
[application]: https://argo-cd.readthedocs.io/en/stable/user-guide/application-specification/
## Quickstart Recap {#quickstart-recap}
In this guide we learned how to:
1. Install Holos.
2. Generate a Git repository for the Platform config.
3. Create a Component that wraps the upstream podinfo Helm Chart without modifications.
4. Render individual components.
5. Mix in an ArgoCD Application resource to every Component in the Platform.

View File

@@ -18,6 +18,14 @@ const sidebars: SidebarsConfig = {
'comparison',
],
api: [
{
label: 'Schema',
type: 'category',
collapsed: false,
items: [
'api/schema/v1alpha3',
],
},
{
label: 'Core API',
type: 'category',

View File

@@ -75,9 +75,6 @@ func (b *buildPlanWrapper) validate() error {
if bp.Kind != v1.BuildPlanKind {
errs = append(errs, fmt.Sprintf("kind invalid: want: %s have: %s", v1alpha1.BuildPlanKind, bp.Kind))
}
if bp.APIVersion != v1.APIVersion {
errs = append(errs, fmt.Sprintf("apiVersion invalid: want: %s have: %s", v1.APIVersion, bp.APIVersion))
}
if len(errs) > 0 {
return errors.New("invalid BuildPlan: " + strings.Join(errs, ", "))
}

View File

@@ -7,7 +7,6 @@ import (
"strings"
"github.com/holos-run/holos/internal/cli/command"
"github.com/holos-run/holos/internal/client"
"github.com/holos-run/holos/internal/errors"
"github.com/holos-run/holos/internal/generate"
"github.com/holos-run/holos/internal/holos"
@@ -35,11 +34,9 @@ func NewPlatform(cfg *holos.Config) *cobra.Command {
cmd.Args = cobra.ExactArgs(1)
cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx := cmd.Root().Context()
clientContext := holos.NewClientContext(ctx)
client := client.New(client.NewConfig(cfg))
for _, name := range args {
if err := generate.GeneratePlatform(ctx, client, clientContext.OrgID, name); err != nil {
if err := generate.GeneratePlatform(ctx, name); err != nil {
return errors.Wrap(err)
}
}

View File

@@ -1,5 +1,8 @@
package holos
// Produce a helm chart build plan.
(#Helm & Chart).Output
let Chart = {
Name: "{{ .Name }}"
Version: "{{ .Version }}"
@@ -10,6 +13,3 @@ let Chart = {
Values: {}
}
// Produce a helm chart build plan.
(#Helm & Chart).Output

View File

@@ -37,27 +37,9 @@ func Platforms() []string {
return dirs
}
func writePlatformMetadata(ctx context.Context, rpc *client.Client, orgID string, name string) error {
func initPlatformMetadata(ctx context.Context, name string) error {
log := logger.FromContext(ctx)
// Link the local platform the SaaS platform ID.
rpcPlatforms, err := rpc.Platforms(ctx, orgID)
if err != nil {
return errors.Wrap(err)
}
var rpcPlatform *platform.Platform
for _, p := range rpcPlatforms {
if p.GetName() == name {
rpcPlatform = p
break
}
log.DebugContext(ctx, "checking platform", "want", name, "have", p.GetName())
}
if rpcPlatform == nil {
return errors.Wrap(errors.New("cannot generate: platform not found in the holos server"))
}
rpcPlatform := &platform.Platform{Name: name}
// Write the platform data.
encoder := protojson.MarshalOptions{Indent: " "}
data, err := encoder.Marshal(rpcPlatform)
@@ -67,7 +49,7 @@ func writePlatformMetadata(ctx context.Context, rpc *client.Client, orgID string
if len(data) > 0 {
data = append(data, '\n')
}
log = log.With("platform_id", rpcPlatform.GetId())
if err := os.WriteFile(client.PlatformMetadataFile, data, 0644); err != nil {
return errors.Wrap(fmt.Errorf("could not write platform metadata: %w", err))
}
@@ -78,7 +60,7 @@ func writePlatformMetadata(ctx context.Context, rpc *client.Client, orgID string
// GeneratePlatform writes the cue code for a platform to the local working
// directory.
func GeneratePlatform(ctx context.Context, rpc *client.Client, orgID string, name string) error {
func GeneratePlatform(ctx context.Context, name string) error {
log := logger.FromContext(ctx)
// Check for a valid platform
platformPath := filepath.Join(platformsRoot, name)
@@ -90,7 +72,7 @@ func GeneratePlatform(ctx context.Context, rpc *client.Client, orgID string, nam
log.DebugContext(ctx, fmt.Sprintf("skipped write %s: already exists", client.PlatformConfigFile))
} else {
if os.IsNotExist(err) {
if err := writePlatformMetadata(ctx, rpc, orgID, name); err != nil {
if err := initPlatformMetadata(ctx, name); err != nil {
return errors.Wrap(err)
}
} else {

View File

@@ -6,11 +6,6 @@ package v1alpha3
import "google.golang.org/protobuf/types/known/structpb"
#PlatformMetadata: {
// Name represents the Platform name.
name: string @go(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
@@ -30,13 +25,18 @@ import "google.golang.org/protobuf/types/known/structpb"
spec: #PlatformSpec @go(Spec)
}
#PlatformMetadata: {
// Name represents the Platform name.
name: string @go(Name)
}
// PlatformSpec represents the specification of a Platform. Think of a platform
// specification as a list of platform components to apply to a list of
// kubernetes clusters combined with the user-specified Platform Model.
#PlatformSpec: {
// Model represents the platform model holos gets from from the
// PlatformService.GetPlatform rpc method and provides to CUE using a tag.
model: structpb.#Struct @go(Model)
model: structpb.#Struct & {...} @go(Model)
// Components represents a list of holos components to manage.
components: [...#PlatformSpecComponent] @go(Components,[]PlatformSpecComponent)

View File

@@ -0,0 +1,94 @@
// Code generated by cue get go. DO NOT EDIT.
//cue:generate cue get go github.com/holos-run/holos/api/schema/v1alpha3
// Package v1alpha3 contains CUE definitions intended as convenience wrappers
// around the core data types defined in package core. The purpose of these
// wrappers is to make life easier for platform engineers by reducing boiler
// plate code and generating component build plans in a consistent manner.
package v1alpha3
import core "github.com/holos-run/holos/api/core/v1alpha3"
// Helm provides a BuildPlan via the Output field which contains one HelmChart
// from package core. Useful as a convenience wrapper to render a HelmChart
// with optional mix-in resources and Kustomization post-processing.
#Helm: {
// Name represents the chart name.
Name: string
// Version represents the chart version.
Version: string
// Namespace represents the helm namespace option when rendering the chart.
Namespace: string
// Resources are kubernetes api objects to mix into the output.
Resources: {...} & {...} @go(,map[string]any)
// Repo represents the chart repository
Repo: {
name: string @go(Name)
url: string @go(URL)
} @go(,"struct{Name string \"json:\\\"name\\\"\"; URL string \"json:\\\"url\\\"\"}")
// Values represents data to marshal into a values.yaml for helm.
Values: _ & {...} @go(,interface{})
// Chart represents the derived HelmChart for inclusion in the BuildPlan
// Output field value. The default HelmChart field values are derived from
// other Helm field values and should be sufficient for most use cases.
Chart: core.#HelmChart
// EnableKustomizePostProcessor processes helm output with kustomize if true.
EnableKustomizePostProcessor: bool & (true | *false)
// KustomizeFiles represents additional files to include in a Kustomization
// resources list. Useful to patch helm output. The implementation is a
// struct with filename keys and structs as values. Holos encodes the struct
// value to yaml then writes the result to the filename key. Component
// authors may then reference the filename in the kustomization.yaml resources
// or patches lists.
// Requires EnableKustomizePostProcessor: true.
KustomizeFiles: {...} & {[string]: {...}} @go(,map[string]any)
// KustomizePatches represents patches to apply to the helm output. Requires
// EnableKustomizePostProcessor: true.
KustomizePatches: {...} & {[string]: {...}} @go(,map[core.InternalLabel]any)
// KustomizeResources represents additional resources files to include in the
// kustomize resources list.
KustomizeResources: {...} & {[string]: {...}} @go(,map[string]any)
// ArgoConfig represents the ArgoCD GitOps configuration for this Component.
ArgoConfig: #ArgoConfig
// Output represents the derived BuildPlan for the Holos cli to render.
Output: core.#BuildPlan
}
// ArgoConfig represents the ArgoCD GitOps configuration for a Component.
// Useful to define once at the root of the Platform configuration and reuse
// across all Components.
#ArgoConfig: {
// Enabled causes holos to render an ArgoCD Application resource for GitOps if true.
Enabled: bool & (true | *false)
// ClusterName represents the cluster within the platform the Application
// resource is intended for.
ClusterName: string
// DeployRoot represents the path from the git repository root to the `deploy`
// rendering output directory. Used as a prefix for the
// Application.spec.source.path field.
DeployRoot: string & (string | *".")
// RepoURL represents the value passed to the Application.spec.source.repoURL
// field.
RepoURL: string
// TargetRevision represents the value passed to the
// Application.spec.source.targetRevision field. Defaults to the branch named
// main.
TargetRevision: string & (string | *"main")
}

View File

@@ -0,0 +1,201 @@
package v1alpha3
import (
"encoding/yaml"
core "github.com/holos-run/holos/api/core/v1alpha3"
kc "sigs.k8s.io/kustomize/api/types"
corev1 "k8s.io/api/core/v1"
appsv1 "k8s.io/api/apps/v1"
rbacv1 "k8s.io/api/rbac/v1"
batchv1 "k8s.io/api/batch/v1"
app "argoproj.io/application/v1alpha1"
)
#Resources: {
[Kind=string]: [InternalLabel=string]: {
kind: Kind
metadata: name: string | *InternalLabel
}
ClusterRole: [_]: rbacv1.#ClusterRole
ClusterRoleBinding: [_]: rbacv1.#ClusterRoleBinding
ConfigMap: [_]: corev1.#ConfigMap
CronJob: [_]: batchv1.#CronJob
Deployment: [_]: appsv1.#Deployment
Job: [_]: batchv1.#Job
Namespace: [_]: corev1.#Namespace
Role: [_]: rbacv1.#Role
RoleBinding: [_]: rbacv1.#RoleBinding
Service: [_]: corev1.#Service
ServiceAccount: [_]: corev1.#ServiceAccount
StatefulSet: [_]: appsv1.#StatefulSet
...
}
#Helm: {
Name: string
Version: string
Namespace: string
Resources: #Resources
Repo: {
name: string | *""
url: string | *""
}
Values: {...}
Chart: core.#HelmChart & {
metadata: name: string | *Name
metadata: namespace: string | *Namespace
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)
// Kustomize post-processor
if EnableKustomizePostProcessor == true {
// 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.
kustomize: resourcesFile: core.#ResourcesFile
// kustomizeFiles represents the files in a kustomize directory tree.
kustomize: kustomizeFiles: core.#FileContentMap
for FileName, Object in KustomizeFiles {
kustomize: kustomizeFiles: "\(FileName)": yaml.Marshal(Object)
}
}
apiObjectMap: (#APIObjects & {apiObjects: Resources}).apiObjectMap
}
// EnableKustomizePostProcessor processes helm output with kustomize if true.
EnableKustomizePostProcessor: true | *false
// KustomizeFiles represents additional files to include in a Kustomization
// resources list. Useful to patch helm output. The implementation is a
// struct with filename keys and structs as values. Holos encodes the struct
// value to yaml then writes the result to the filename key. Component
// authors may then reference the filename in the kustomization.yaml resources
// or patches lists.
// Requires EnableKustomizePostProcessor: true.
KustomizeFiles: {
// Embed KustomizeResources
KustomizeResources
// The kustomization.yaml file must be included for kustomize to work.
"kustomization.yaml": kc.#Kustomization & {
apiVersion: "kustomize.config.k8s.io/v1beta1"
kind: "Kustomization"
resources: [core.#ResourcesFile, for FileName, _ in KustomizeResources {FileName}]
patches: [for x in KustomizePatches {x}]
}
}
// KustomizePatches represents patches to apply to the helm output. Requires
// EnableKustomizePostProcessor: true.
KustomizePatches: [ArbitraryLabel=string]: kc.#Patch
// KustomizeResources represents additional resources files to include in the
// kustomize resources list.
KustomizeResources: [FileName=string]: {...}
// ArgoConfig represents the ArgoCD GitOps integration for this Component.
ArgoConfig: _
// output represents the build plan provided to the holos cli.
Output: #BuildPlan & {
_Name: Name
_Namespace: Namespace
_ArgoConfig: ArgoConfig
spec: components: helmChartList: [Chart]
}
}
#BuildPlan: core.#BuildPlan & {
_Name: string
_Namespace?: string
_ArgoConfig: #ArgoConfig
if _ArgoConfig.Enabled {
let NAME = "gitops/\(_Name)"
// Render the ArgoCD Application for GitOps as an additional Component of
// the BuildPlan.
spec: components: resources: (NAME): {
metadata: name: NAME
if _Namespace != _|_ {
metadata: namespace: _Namespace
}
deployFiles: (#Argo & {ComponentName: _Name, ArgoConfig: _ArgoConfig}).deployFiles
}
}
}
// #Argo represents an argocd Application resource for each component, written
// using the #HolosComponent.deployFiles field.
#Argo: {
ComponentName: string
ArgoConfig: #ArgoConfig
Application: app.#Application & {
metadata: name: ComponentName
metadata: namespace: "argocd"
spec: {
destination: server: "https://kubernetes.default.svc"
project: "default"
source: {
path: "\(ArgoConfig.DeployRoot)/deploy/clusters/\(ArgoConfig.ClusterName)/components/\(ComponentName)"
repoURL: ArgoConfig.RepoURL
targetRevision: ArgoConfig.TargetRevision
}
}
}
// deployFiles represents the output files to write along side the component.
deployFiles: "clusters/\(ArgoConfig.ClusterName)/gitops/\(ComponentName).application.gen.yaml": yaml.Marshal(Application)
}
// #ArgoDefaultSyncPolicy represents the default argo sync policy.
#ArgoDefaultSyncPolicy: {
automated: {
prune: bool | *true
selfHeal: bool | *true
}
syncOptions: [
"RespectIgnoreDifferences=true",
"ServerSideApply=true",
]
retry: limit: number | *2
retry: backoff: {
duration: string | *"5s"
factor: number | *2
maxDuration: string | *"3m0s"
}
}
// #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: core.#APIObjects & {
// apiObjects represents the un-marshalled form of each kubernetes api object
// managed by a holos component.
apiObjects: {
[Kind=string]: {
[string]: {
kind: Kind
...
}
}
}
// apiObjectMap holds the marshalled representation of apiObjects
for kind, v in apiObjects {
for name, obj in v {
apiObjectMap: (kind): (name): yaml.Marshal(obj)
}
}
}

View File

@@ -9,6 +9,9 @@ package platforms
//go generate rm -rf cue.mod/gen/github.com/holos-run/holos/api/meta
//go:generate cue get go github.com/holos-run/holos/api/meta/...
//go generate rm -rf cue.mod/gen/github.com/holos-run/holos/api/schema
//go:generate cue get go github.com/holos-run/holos/api/schema/...
//go generate rm -rf cue.mod/gen/github.com/holos-run/holos/service/gen/holos/object
//go:generate cue import ../../../service/holos/object/v1alpha1/object.proto -o cue.mod/gen/github.com/holos-run/holos/service/gen/holos/object/v1alpha1/object.proto_gen.cue -I ../../../proto -f
//go:generate rm -f cue.mod/gen/github.com/holos-run/holos/service/gen/holos/object/v1alpha1/object.pb_go_gen.cue

View File

@@ -0,0 +1,8 @@
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
vendor/

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,7 @@
package holos
import core "github.com/holos-run/holos/api/core/v1alpha3"
core.#Platform & {
metadata: name: "quickstart"
}

View File

@@ -0,0 +1,5 @@
# Holos Quickstart
Generated from the [Holos Quickstart][quickstart] guide.
[quickstart]: https://holos.run/docs/quickstart/

View File

@@ -0,0 +1,11 @@
package holos
import schema "github.com/holos-run/holos/api/schema/v1alpha3"
#Helm: schema.#Helm & {
ArgoConfig: #ArgoConfig
}
#ArgoConfig: schema.#ArgoConfig & {
ClusterName: _ClusterName
}

View File

@@ -0,0 +1,14 @@
package holos
import (
"encoding/json"
dto "github.com/holos-run/holos/service/gen/holos/object/v1alpha1:object"
)
// _ClusterName is the --cluster-name flag value provided by the holos cli.
_ClusterName: string @tag(cluster, type=string)
// _PlatformConfig represents all of the data passed from holos to cue, used to
// carry the platform and project models.
_PlatformConfig: dto.#PlatformConfig & json.Unmarshal(_PlatformConfigJSON)
_PlatformConfigJSON: string | *"{}" @tag(platform_config, type=string)

View File

@@ -1 +1 @@
0
1