Compare commits

..

5 Commits

Author SHA1 Message Date
Jeff McCune
2e2ed398c6 (#175) Fix tests 2024-05-20 11:32:29 -07:00
Jeff McCune
34f2a52cb7 (#175) Add holos render platform command
Split holos render into component and platform.

This patch splits the previous `holos render` command into subcommands.
`holos render component ./path/to/component/` behaves as the previous
`holos render` command and renders an individual component.

The new `holos render platform ./path/to/platform/` subcommand makes
space to render the entire platform using the platform model pulled from
the PlatformService.

Starting with an empty directory:

```sh
holos register user
holos generate platform bare
holos pull platform config .
holos render platform ./platform/
```

```txt
10:01AM INF platform.go:29 ok render component version=0.80.2 path=components/configmap cluster=k1 num=1 total=1 duration=448.133038ms
```

The bare platform has a single component which refers to the platform
model pulled from the PlatformService:

```sh
cat deploy/clusters/mycluster/components/platform-configmap/platform-configmap.gen.yaml
```

```yaml
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: platform
  namespace: default
data:
  platform: |
    spec:
      model:
        cloud:
          providers:
            - cloudflare
        cloudflare:
          email: platform@openinfrastructure.co
        org:
          displayName: Open Infrastructure Services
          name: ois
```
2024-05-20 10:41:24 -07:00
Jeff McCune
d3888a884f (#175) go mod tidy 2024-05-20 06:32:53 -07:00
Jeff McCune
3845871368 (#175) holos pull platform config
This patch adds a subcommand to pull the data necessary to construct a
PlatformConfig DTO.  The PlatformConfig message contains all of the
fields and values necessary to build a platform and the platform
components.  This is an alternative to holos passing multiple tags to
CUE.  The PlatformConfig is marshalled and passed once.

The platform config is also stored in the local filesystem in the root
directory of the platform.  This enables repeated local building and
rendering without making an rpc call.

The build / render pipeline is expected to cache the PlatformConfig once
at the start of the pipeline using the pull subcommand.
2024-05-19 08:27:21 -07:00
Jeff McCune
a3b2d19adb (#175) Render the platform with the model
The `holos render platform` command is unimplemented.  This patch
partially implements platform rendering by fetching the platform model
from the PlatformService and providing it to CUE using a tag.

CUE returns a `kind: Platform` resource to `holos` which will eventually
process a Buildlan for each platform component listed in the Platform
spec.

For now, however, it's sufficient to have the current platform model
available to CUE.
2024-05-18 11:40:30 -07:00
35 changed files with 677 additions and 102 deletions

View File

@@ -9,8 +9,8 @@ import "google.golang.org/protobuf/types/known/structpb"
// model, and holos components to build into one resource.
type Platform struct {
TypeMeta `json:",inline" yaml:",inline"`
Metadata ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
Spec PlatformSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
Metadata ObjectMeta `json:"metadata" yaml:"metadata"`
Spec PlatformSpec `json:"spec" yaml:"spec"`
}
// PlatformSpec represents the platform build plan specification.
@@ -18,5 +18,15 @@ type PlatformSpec struct {
// Model represents the platform model holos gets from from the
// holos.platform.v1alpha1.PlatformService.GetPlatform method and provides to
// CUE using a tag.
Model structpb.Struct `json:"model,omitempty" yaml:"model,omitempty"`
Model structpb.Struct `json:"model" yaml:"model"`
Components []PlatformSpecComponent `json:"components" yaml:"components"`
}
// PlatformSpecComponent represents a component to build or render with flags to
// pass, for example the cluster name.
type PlatformSpecComponent struct {
// Path is the path of the component relative to the platform root.
Path string `json:"path" yaml:"path"`
// Cluster is the cluster name to use when building the component.
Cluster string `json:"cluster" yaml:"cluster"`
}

View File

@@ -2,6 +2,8 @@
exec holos build ./foo/... --log-level debug
stdout '^bf2bc7f9-9ba0-4f9e-9bd2-9a205627eb0b$'
-- platform.config.json --
{}
-- cue.mod --
package holos
-- foo/constraints.cue --
@@ -20,6 +22,7 @@ spec: components: KubernetesObjectsList: [
package holos
_cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
#KubernetesObjects: {
apiVersion: "holos.run/v1alpha1"

View File

@@ -3,12 +3,15 @@
stderr 'apiObjectMap.foo.bar: cannot convert incomplete value'
stderr '/component.cue:\d+:\d+$'
-- platform.config.json --
{}
-- cue.mod --
package holos
-- component.cue --
package holos
_cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"

View File

@@ -3,6 +3,8 @@ exec holos build .
stdout '^kind: SecretStore$'
stdout '# Source: CUE apiObjects.SecretStore.default'
-- platform.config.json --
{}
-- cue.mod --
package holos
-- component.cue --
@@ -13,6 +15,7 @@ kind: "BuildPlan"
spec: components: KubernetesObjectsList: [{apiObjectMap: #APIObjects.apiObjectMap}]
_cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
#SecretStore: {
kind: string

View File

@@ -4,6 +4,8 @@ stdout '^kind: SecretStore$'
stdout '# Source: CUE apiObjects.SecretStore.default'
stderr 'skipping helm: no chart name specified'
-- platform.config.json --
{}
-- cue.mod --
package holos
-- component.cue --
@@ -14,6 +16,7 @@ kind: "BuildPlan"
spec: components: HelmChartList: [{apiObjectMap: #APIObjects.apiObjectMap}]
_cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
#SecretStore: {
kind: string

View File

@@ -2,6 +2,8 @@
! exec holos build .
stderr 'apiObjects.secretstore.default.foo: field not allowed'
-- platform.config.json --
{}
-- cue.mod --
package holos
-- component.cue --
@@ -10,6 +12,7 @@ package holos
apiVersion: "holos.run/v1alpha1"
kind: "KubernetesObjects"
cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
#SecretStore: {
metadata: name: string

View File

@@ -2,6 +2,8 @@
! exec holos build .
stderr 'Error: execution error at \(zitadel/templates/secret_zitadel-masterkey.yaml:2:4\): Either set .Values.zitadel.masterkey xor .Values.zitadel.masterkeySecretName'
-- platform.config.json --
{}
-- cue.mod --
package holos
-- zitadel.cue --
@@ -12,6 +14,7 @@ kind: "BuildPlan"
spec: components: HelmChartList: [_HelmChart]
_cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
_HelmChart: {
apiVersion: "holos.run/v1alpha1"

View File

@@ -1,15 +1,18 @@
# Kustomize is a supported holos component kind
exec holos render --cluster-name=mycluster . --log-level=debug
exec holos render component --cluster-name=mycluster . --log-level=debug
# Want generated output
cmp want.yaml deploy/clusters/mycluster/components/kstest/kstest.gen.yaml
-- platform.config.json --
{}
-- cue.mod --
package holos
-- component.cue --
package holos
_cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"

View File

@@ -3,11 +3,14 @@
! exec holos build .
stderr 'unknown field \\"TypoKubernetesObjectsList\\"'
-- platform.config.json --
{}
-- cue.mod --
package holos
-- component.cue --
package holos
_cluster: string @tag(cluster, string)
_platform_config: string @tag(platform_config, string)
apiVersion: "holos.run/v1alpha1"
kind: "BuildPlan"

2
go.mod
View File

@@ -16,7 +16,6 @@ require (
github.com/fullstorydev/grpcurl v1.9.1
github.com/go-jose/go-jose/v3 v3.0.3
github.com/gofrs/uuid v4.4.0+incompatible
github.com/gogo/protobuf v1.3.2
github.com/int128/kubelogin v1.28.0
github.com/jackc/pgx/v5 v5.5.5
github.com/lmittmann/tint v1.0.4
@@ -115,6 +114,7 @@ require (
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/uuid/v5 v5.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect

View File

@@ -17,6 +17,7 @@ import (
"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"
)
@@ -70,7 +71,7 @@ func (b *Builder) Cluster() string {
}
// Instances returns the cue build instances being built.
func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
func (b *Builder) Instances(ctx context.Context, cfg *client.Config) ([]*build.Instance, error) {
log := logger.FromContext(ctx)
mod, err := b.findCueMod()
@@ -79,7 +80,18 @@ func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
}
dir := string(mod)
cfg := load.Config{Dir: dir}
cueConfig := load.Config{Dir: dir}
// Get the platform model from the PlatformConfig
pc, err := client.LoadPlatformConfig(ctx, dir)
if err != nil {
return nil, errors.Wrap(err)
}
data, err := json.Marshal(pc)
if err != nil {
return nil, errors.Wrap(err)
}
cueConfig.Tags = append(cueConfig.Tags, "platform_config="+string(data))
// Make args relative to the module directory
args := make([]string, len(b.cfg.args))
@@ -100,17 +112,17 @@ func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
// Refer to https://github.com/cue-lang/cue/blob/v0.7.0/cmd/cue/cmd/common.go#L429
if b.Cluster() != "" {
cfg.Tags = append(cfg.Tags, "cluster="+b.Cluster())
cueConfig.Tags = append(cueConfig.Tags, "cluster="+b.Cluster())
}
log.DebugContext(ctx, fmt.Sprintf("cue: tags %v", cfg.Tags))
log.DebugContext(ctx, fmt.Sprintf("cue: tags %v", cueConfig.Tags))
return load.Instances(args, &cfg), nil
return load.Instances(args, &cueConfig), nil
}
func (b *Builder) Run(ctx context.Context) (results []*v1alpha1.Result, err error) {
func (b *Builder) Run(ctx context.Context, cfg *client.Config) (results []*v1alpha1.Result, err error) {
log := logger.FromContext(ctx)
log.DebugContext(ctx, "cue: building instances")
instances, err := b.Instances(ctx)
instances, err := b.Instances(ctx, cfg)
if err != nil {
return nil, err
}
@@ -166,6 +178,7 @@ 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
if err = decoder.Decode(&bp); err != nil {
@@ -173,13 +186,6 @@ func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (res
return
}
results, err = b.buildPlan(ctx, &bp, path)
case "Platform":
var pf v1alpha1.Platform
if err = decoder.Decode(&pf); err != nil {
err = errors.Wrap(fmt.Errorf("could not decode Platform %s: %w", instance.Dir, err))
return
}
results, err = b.buildPlatform(ctx, &pf)
default:
err = errors.Wrap(fmt.Errorf("unknown kind: %v", tm.Kind))
}
@@ -187,12 +193,6 @@ func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (res
return
}
func (b *Builder) buildPlatform(ctx context.Context, pf *v1alpha1.Platform) (results []*v1alpha1.Result, err error) {
log := logger.FromContext(ctx)
log.ErrorContext(ctx, "not implemented", "platform", pf)
return nil, errors.Wrap(fmt.Errorf("not implemeneted"))
}
func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha1.BuildPlan, path holos.InstancePath) (results []*v1alpha1.Result, err error) {
log := logger.FromContext(ctx)

View File

@@ -0,0 +1,89 @@
package builder
import (
"bytes"
"context"
"encoding/json"
"fmt"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/cuecontext"
"github.com/holos-run/holos"
"github.com/holos-run/holos/api/v1alpha1"
"github.com/holos-run/holos/internal/client"
"github.com/holos-run/holos/internal/errors"
"github.com/holos-run/holos/internal/logger"
)
// Platform builds a platform
func (b *Builder) Platform(ctx context.Context, cfg *client.Config) (*v1alpha1.Platform, error) {
log := logger.FromContext(ctx)
log.DebugContext(ctx, "cue: building platform instance")
instances, err := b.Instances(ctx, cfg)
if err != nil {
return nil, errors.Wrap(err)
}
// We only process the first instance, assume the render platform subcommand enforces this.
for idx, instance := range instances {
log.DebugContext(ctx, "cue: building instance", "idx", idx, "dir", instance.Dir)
p, err := b.runPlatform(ctx, instance)
if err != nil {
return nil, errors.Wrap(fmt.Errorf("could not build platform: %w", err))
}
return p, nil
}
return nil, errors.Wrap(errors.New("missing platform instance"))
}
func (b Builder) runPlatform(ctx context.Context, instance *build.Instance) (*v1alpha1.Platform, error) {
path := holos.InstancePath(instance.Dir)
log := logger.FromContext(ctx).With("dir", path)
if err := instance.Err; err != nil {
return nil, errors.Wrap(fmt.Errorf("could not load: %w", err))
}
cueCtx := cuecontext.New()
value := cueCtx.BuildInstance(instance)
if err := value.Err(); err != nil {
return nil, errors.Wrap(fmt.Errorf("could not build %s: %w", instance.Dir, err))
}
log.DebugContext(ctx, "cue: validating instance")
if err := value.Validate(); err != nil {
return nil, errors.Wrap(fmt.Errorf("could not validate: %w", err))
}
log.DebugContext(ctx, "cue: decoding holos platform")
jsonBytes, err := value.MarshalJSON()
if err != nil {
return nil, errors.Wrap(fmt.Errorf("could not marshal cue instance %s: %w", instance.Dir, err))
}
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
// Discriminate the type of build plan.
tm := &v1alpha1.TypeMeta{}
err = decoder.Decode(tm)
if err != nil {
return nil, errors.Wrap(fmt.Errorf("invalid platform: %s: %w", instance.Dir, err))
}
log.DebugContext(ctx, "cue: discriminated build kind: "+tm.Kind, "kind", tm.Kind, "apiVersion", tm.APIVersion)
// New decoder for the full object
decoder = json.NewDecoder(bytes.NewReader(jsonBytes))
decoder.DisallowUnknownFields()
var pf v1alpha1.Platform
switch tm.Kind {
case "Platform":
if err = decoder.Decode(&pf); err != nil {
err = errors.Wrap(fmt.Errorf("could not decode platform %s: %w", instance.Dir, err))
return nil, err
}
return &pf, nil
default:
err = errors.Wrap(fmt.Errorf("unknown kind: %v", tm.Kind))
}
return nil, err
}

View File

@@ -7,16 +7,18 @@ import (
"github.com/holos-run/holos/internal/builder"
"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/holos"
"github.com/spf13/cobra"
)
// makeBuildRunFunc returns the internal implementation of the build cli command
func makeBuildRunFunc(cfg *holos.Config) command.RunFunc {
func makeBuildRunFunc(cfg *client.Config) command.RunFunc {
return func(cmd *cobra.Command, args []string) error {
build := builder.New(builder.Entrypoints(args), builder.Cluster(cfg.ClusterName()))
results, err := build.Run(cmd.Context())
ctx := cmd.Root().Context()
build := builder.New(builder.Entrypoints(args), builder.Cluster(cfg.Holos().ClusterName()))
results, err := build.Run(ctx, cfg)
if err != nil {
return err
}
@@ -42,7 +44,12 @@ func New(cfg *holos.Config) *cobra.Command {
cmd := command.New("build [directory...]")
cmd.Args = cobra.MinimumNArgs(1)
cmd.Short = "build kubernetes api objects from a directory"
cmd.RunE = makeBuildRunFunc(cfg)
cmd.Flags().AddGoFlagSet(cfg.ClusterFlagSet())
config := client.NewConfig(cfg)
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
cmd.RunE = makeBuildRunFunc(config)
return cmd
}

View File

@@ -1,9 +1,6 @@
package command
import (
"fmt"
"github.com/holos-run/holos/internal/errors"
"github.com/holos-run/holos/version"
"github.com/spf13/cobra"
)
@@ -20,9 +17,6 @@ func New(name string) *cobra.Command {
CompletionOptions: cobra.CompletionOptions{
HiddenDefaultCmd: true,
},
RunE: func(c *cobra.Command, args []string) error {
return errors.Wrap(fmt.Errorf("could not run %v: not implemented", c.Name()))
},
SilenceUsage: true,
SilenceErrors: true,
}

81
internal/cli/pull/pull.go Normal file
View File

@@ -0,0 +1,81 @@
// Package pull pulls resources from the PlatformService and caches them in the
// local filesystem.
package pull
import (
"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/holos"
"github.com/holos-run/holos/internal/server/middleware/logger"
object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
"github.com/spf13/cobra"
)
func New(cfg *holos.Config) *cobra.Command {
cmd := command.New("pull")
cmd.Short = "pull resources from holos server"
cmd.Args = cobra.NoArgs
config := client.NewConfig(cfg)
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
cmd.AddCommand(NewPlatform(config))
return cmd
}
func NewPlatform(cfg *client.Config) *cobra.Command {
cmd := command.New("platform")
cmd.Short = "pull platform resources"
cmd.Args = cobra.NoArgs
cmd.AddCommand(NewPlatformConfig(cfg))
return cmd
}
func NewPlatformConfig(cfg *client.Config) *cobra.Command {
cmd := command.New("config")
cmd.Short = "pull platform config"
cmd.Args = cobra.MinimumNArgs(1)
cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx := cmd.Root().Context()
if ctx == nil {
return errors.Wrap(errors.New("cannot execute: no context"))
}
ctx = logger.NewContext(ctx, logger.FromContext(ctx).With("server", cfg.Client().Server()))
rpc := client.New(cfg)
for _, name := range args {
// Get the platform metadata for the platform id.
pmd, err := client.LoadPlatform(ctx, name)
if err != nil {
return errors.Wrap(err)
}
log := logger.FromContext(ctx).With("platform_id", pmd.GetId())
// Get the platform model
model, err := rpc.PlatformModel(ctx, pmd.GetId())
if err != nil {
return errors.Wrap(err)
}
log.Info("pulled platform model")
// Build the PlatformConfig
pc := &object.PlatformConfig{
PlatformId: pmd.GetId(),
PlatformModel: model,
}
// Save the PlatformConfig
path, err := client.SavePlatformConfig(ctx, name, pc)
if err != nil {
return errors.Wrap(err)
}
log.Info("saved platform config", "path", path)
}
return nil
}
return cmd
}

View File

@@ -54,7 +54,7 @@ func NewPlatformForm(cfg *client.Config) *cobra.Command {
rpc := client.New(cfg)
for _, name := range args {
// Get the platform metadata for the platform id.
p, err := push.LoadPlatform(ctx, name)
p, err := client.LoadPlatform(ctx, name)
if err != nil {
return errors.Wrap(err)
}

View File

@@ -7,21 +7,35 @@ import (
"github.com/holos-run/holos/internal/builder"
"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/holos"
"github.com/holos-run/holos/internal/logger"
"github.com/holos-run/holos/internal/render"
"github.com/spf13/cobra"
)
// New returns the render subcommand for the root command
func New(cfg *holos.Config) *cobra.Command {
cmd := command.New("render [directory...]")
cmd := command.New("render")
cmd.Args = cobra.NoArgs
cmd.Short = "render platform configuration"
cmd.AddCommand(NewComponent(cfg))
cmd.AddCommand(NewPlatform(cfg))
return cmd
}
// New returns the component subcommand for the render command
func NewComponent(cfg *holos.Config) *cobra.Command {
cmd := command.New("component [directory...]")
cmd.Args = cobra.MinimumNArgs(1)
cmd.Short = "write kubernetes api objects to the filesystem"
cmd.Flags().SortFlags = false
cmd.Flags().AddGoFlagSet(cfg.WriteFlagSet())
cmd.Flags().AddGoFlagSet(cfg.ClusterFlagSet())
config := client.NewConfig(cfg)
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
var printInstances bool
flagSet := flag.NewFlagSet("", flag.ContinueOnError)
flagSet.BoolVar(&printInstances, "print-instances", false, "expand /... paths for xargs")
@@ -33,7 +47,7 @@ func New(cfg *holos.Config) *cobra.Command {
build := builder.New(builder.Entrypoints(args), builder.Cluster(cfg.ClusterName()))
if printInstances {
instances, err := build.Instances(ctx)
instances, err := build.Instances(ctx, config)
if err != nil {
return errors.Wrap(err)
}
@@ -43,13 +57,14 @@ func New(cfg *holos.Config) *cobra.Command {
return nil
}
results, err := build.Run(cmd.Context())
results, err := build.Run(ctx, config)
if err != nil {
return errors.Wrap(err)
}
// TODO: Avoid accidental over-writes if to holos component instances result in
// the same file path. Write files into a blank temporary directory, error if a
// file exists, then move the directory into place.
// TODO: Avoid accidental over-writes if two or more holos component
// instances result in the same file path. Write files into a blank
// temporary directory, error if a file exists, then move the directory into
// place.
var result Result
for _, result = range results {
if result.Continue() {
@@ -72,6 +87,30 @@ func New(cfg *holos.Config) *cobra.Command {
return cmd
}
func NewPlatform(cfg *holos.Config) *cobra.Command {
cmd := command.New("platform [directory]")
cmd.Args = cobra.ExactArgs(1)
cmd.Short = "render all platform components"
config := client.NewConfig(cfg)
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
cmd.RunE = func(cmd *cobra.Command, args []string) error {
ctx := cmd.Root().Context()
build := builder.New(builder.Entrypoints(args))
platform, err := build.Platform(ctx, config)
if err != nil {
return errors.Wrap(err)
}
return render.Platform(ctx, platform, cmd.ErrOrStderr())
}
return cmd
}
type Result interface {
Continue() bool
Name() string

View File

@@ -22,6 +22,7 @@ import (
"github.com/holos-run/holos/internal/cli/login"
"github.com/holos-run/holos/internal/cli/logout"
"github.com/holos-run/holos/internal/cli/preflight"
"github.com/holos-run/holos/internal/cli/pull"
"github.com/holos-run/holos/internal/cli/push"
"github.com/holos-run/holos/internal/cli/register"
"github.com/holos-run/holos/internal/cli/render"
@@ -73,6 +74,7 @@ func New(cfg *holos.Config) *cobra.Command {
rootCmd.AddCommand(rpc.New(cfg))
rootCmd.AddCommand(generate.New(cfg))
rootCmd.AddCommand(register.New(cfg))
rootCmd.AddCommand(pull.New(cfg))
rootCmd.AddCommand(push.New(cfg))
rootCmd.AddCommand(newOrgCmd())

View File

@@ -1,7 +0,0 @@
# Want no hash appended
holos create secret test --namespace holos-system --from-file $WORK/test --append-hash=false
stderr ' created: test '
stderr ' secret=test '
-- test --
sekret

View File

@@ -15,6 +15,7 @@ import (
"github.com/holos-run/holos/service/gen/holos/platform/v1alpha1/platformconnect"
"github.com/holos-run/holos/service/gen/holos/user/v1alpha1/userconnect"
"google.golang.org/protobuf/types/known/fieldmaskpb"
"google.golang.org/protobuf/types/known/structpb"
)
func New(cfg *Config) *Client {
@@ -67,3 +68,19 @@ func (c *Client) UpdateForm(ctx context.Context, platformID string, form *object
log.DebugContext(ctx, "updated platform", "platform_id", platformID, "duration", time.Since(start))
return nil
}
// PlatformModel gets the platform model from the PlatformService.
func (c *Client) PlatformModel(ctx context.Context, platformID string) (*structpb.Struct, error) {
start := time.Now()
req := &platform.GetPlatformRequest{
PlatformId: platformID,
FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"spec.model"}},
}
pf, err := c.pltSvc.GetPlatform(ctx, connect.NewRequest(req))
if err != nil {
return nil, errors.Wrap(err)
}
log := logger.FromContext(ctx)
log.DebugContext(ctx, "get platform", "platform_id", platformID, "duration", time.Since(start))
return pf.Msg.GetPlatform().GetSpec().GetModel(), nil
}

View File

@@ -61,3 +61,11 @@ func (c *Config) Context() *holos.ClientContext {
}
return c.context
}
// Holos returns the *holos.Config
func (c *Config) Holos() *holos.Config {
if c == nil {
return nil
}
return c.holos
}

View File

@@ -0,0 +1,68 @@
package client
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/holos-run/holos/internal/server/middleware/logger"
object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
"google.golang.org/protobuf/encoding/protojson"
)
// PlatformMetadataFile is the platform metadata json file name located in the root
// of a platform directory.
const PlatformMetadataFile = "platform.metadata.json"
// PlatformConfigFile is the marshaled json representation of the PlatformConfig
// DTO used to cache the data holos passes from the PlatformService to CUE when
// rendering platform components.
const PlatformConfigFile = "platform.config.json"
// LoadPlatform loads the platform.metadata.json file from a named path. Useful
// to obtain a platform id for PlatformService rpc methods.
func LoadPlatform(ctx context.Context, name string) (*platform.Platform, error) {
data, err := os.ReadFile(filepath.Join(name, PlatformMetadataFile))
if err != nil {
return nil, fmt.Errorf("could not load platform metadata: %w", err)
}
p := &platform.Platform{}
if err := protojson.Unmarshal(data, p); err != nil {
return nil, fmt.Errorf("could not load platform metadata: %w", err)
}
return p, nil
}
// LoadPlatformConfig loads the PlatformConfig DTO from the platform.config.json
// file. Useful to provide all values necessary to render cue config without an
// rpc to the HolosService.
func LoadPlatformConfig(ctx context.Context, name string) (*object.PlatformConfig, error) {
data, err := os.ReadFile(filepath.Join(name, PlatformConfigFile))
if err != nil {
return nil, fmt.Errorf("could not load platform config: %w", err)
}
pc := &object.PlatformConfig{}
if err := protojson.Unmarshal(data, pc); err != nil {
return nil, fmt.Errorf("could not load platform config: %w", err)
}
return pc, nil
}
// SavePlatformConfig writes pc to the platform root directory path identified by name.
func SavePlatformConfig(ctx context.Context, name string, pc *object.PlatformConfig) (string, error) {
data, err := protojson.Marshal(pc)
if err != nil {
return "", err
}
if len(data) > 0 {
data = append(data, '\n')
}
path := filepath.Join(name, PlatformConfigFile)
if err := os.WriteFile(path, data, 0644); err != nil {
return "", fmt.Errorf("could not write platform config: %w", err)
}
logger.FromContext(ctx).DebugContext(ctx, "wrote", "path", path)
return path, nil
}

View File

@@ -367,3 +367,53 @@ export class Form extends Message<Form> {
}
}
/**
* PlatformConfig represents the data passed from the holos cli to CUE when
* rendering configuration.
*
* @generated from message holos.object.v1alpha1.PlatformConfig
*/
export class PlatformConfig extends Message<PlatformConfig> {
/**
* Platform UUID.
*
* @generated from field: string platform_id = 1;
*/
platformId = "";
/**
* Platform Model.
*
* @generated from field: google.protobuf.Struct platform_model = 2;
*/
platformModel?: Struct;
constructor(data?: PartialMessage<PlatformConfig>) {
super();
proto3.util.initPartial(data, this);
}
static readonly runtime: typeof proto3 = proto3;
static readonly typeName = "holos.object.v1alpha1.PlatformConfig";
static readonly fields: FieldList = proto3.util.newFieldList(() => [
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
{ no: 2, name: "platform_model", kind: "message", T: Struct },
]);
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformConfig {
return new PlatformConfig().fromBinary(bytes, options);
}
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PlatformConfig {
return new PlatformConfig().fromJson(jsonValue, options);
}
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PlatformConfig {
return new PlatformConfig().fromJsonString(jsonString, options);
}
static equals(a: PlatformConfig | PlainMessage<PlatformConfig> | undefined, b: PlatformConfig | PlainMessage<PlatformConfig> | undefined): boolean {
return proto3.util.equals(PlatformConfig, a, b);
}
}

View File

@@ -72,11 +72,10 @@ func GeneratePlatform(ctx context.Context, rpc *client.Client, orgID string, nam
data = append(data, '\n')
}
log = log.With("platform_id", rpcPlatform.GetId())
path := "platform.metadata.json"
if err := os.WriteFile(path, data, 0644); err != nil {
if err := os.WriteFile(client.PlatformMetadataFile, data, 0644); err != nil {
return errors.Wrap(fmt.Errorf("could not write platform metadata: %w", err))
}
log.InfoContext(ctx, "wrote "+path, "path", filepath.Join(getCwd(ctx), path))
log.InfoContext(ctx, "wrote "+client.PlatformMetadataFile, "path", filepath.Join(getCwd(ctx), client.PlatformMetadataFile))
// Copy the cue.mod directory
if err := copyEmbedFS(ctx, platforms, filepath.Join(root, "cue.mod"), "cue.mod"); err != nil {

View File

@@ -3,8 +3,6 @@ package holos
import "encoding/yaml"
import v1 "github.com/holos-run/holos/api/v1alpha1"
let PLATFORM = {message: "TODO: Load the platform from the API."}
// Provide a BuildPlan to the holos cli to render k8s api objects.
v1.#BuildPlan & {
spec: components: resources: platformConfigmap: {
@@ -20,6 +18,12 @@ let OBJECTS = v1.#APIObjects & {
name: "platform"
namespace: "default"
}
// Output the platform model which is derived from the web app form the
// platform engineer provides and the form values the end user provides.
data: platform: yaml.Marshal(PLATFORM)
}
}
let PLATFORM = {
spec: model: _Platform.spec.model
}

View File

@@ -3,6 +3,12 @@ package holos
import "encoding/json"
import v1 "github.com/holos-run/holos/api/v1alpha1"
import dto "github.com/holos-run/holos/service/gen/holos/object/v1alpha1:object"
// _PlatformConfig represents all of the data passed from holos to cue.
// Intended to carry the platform model and project models.
_PlatformConfig: dto.#PlatformConfig & json.Unmarshal(_PlatformConfigJSON)
_PlatformConfigJSON: string | *"{}" @tag(platform_config, type=string)
// _Platform provides a platform resource to the holos cli for rendering. The
// field is hidden because most components need to refer to platform data,
@@ -15,8 +21,24 @@ _Platform: v1.#Platform & {
// spec is the platform specification
spec: {
// model represents the web form values provided by the user.
model: json.Unmarshal(_model)
// _model is the json representation of model injected into CUE from holos.
_model: string | *"{}" @tag(platform_model, type=string)
model: _PlatformConfig.platform_model
components: [for c in _components {c}]
_components: [string]: v1.#PlatformSpecComponent
_components: {
for WorkloadCluster in _Clusters.Workload {
"\(WorkloadCluster)-configmap": {
path: "components/configmap"
cluster: WorkloadCluster
}
}
}
}
}
// _Clusters represents the clusters in the platform. The default values are
// intended to be provided by the user in a file which is not written over by
// `holos generate`.
_Clusters: {
Workload: [...string] | *["mycluster"]
}

View File

@@ -13,8 +13,8 @@ import "google.golang.org/protobuf/types/known/structpb"
// model, and holos components to build into one resource.
#Platform: {
#TypeMeta
metadata?: #ObjectMeta @go(Metadata)
spec?: #PlatformSpec @go(Spec)
metadata: #ObjectMeta @go(Metadata)
spec: #PlatformSpec @go(Spec)
}
// PlatformSpec represents the platform build plan specification.
@@ -22,5 +22,16 @@ import "google.golang.org/protobuf/types/known/structpb"
// Model represents the platform model holos gets from from the
// holos.platform.v1alpha1.PlatformService.GetPlatform method and provides to
// CUE using a tag.
model?: structpb.#Struct @go(Model)
model: structpb.#Struct @go(Model)
components: [...#PlatformSpecComponent] @go(Components,[]PlatformSpecComponent)
}
// PlatformSpecComponent represents a component to build or render with flags to
// pass, for example the cluster name.
#PlatformSpecComponent: {
// Path is the path of the component relative to the platform root.
path: string @go(Path)
// Cluster is the cluster name to use when building the component.
cluster: string @go(Cluster)
}

View File

@@ -114,3 +114,13 @@ _#isResourceOwner_ResourceOwner: _
// organized by section.
field_configs?: [...null | structpb.#Struct] @go(FieldConfigs,[]*structpb.Struct) @protobuf(1,bytes,rep,json=fieldConfigs,proto3)
}
// PlatformConfig represents the data passed from the holos cli to CUE when
// rendering configuration.
#PlatformConfig: {
// Platform UUID.
platform_id?: string @go(PlatformId) @protobuf(1,bytes,opt,json=platformId,proto3)
// Platform Model.
platform_model?: null | structpb.#Struct @go(PlatformModel,*structpb.Struct) @protobuf(2,bytes,opt,json=platformModel,proto3)
}

View File

@@ -0,0 +1,16 @@
package object
// Override the optional fields which result from the omitempty struct tags.
// Make them required so they work with yaml.Marshal
import "google.golang.org/protobuf/types/known/structpb"
// PlatformConfig represents the data passed from the holos cli to CUE when
// rendering configuration.
#PlatformConfig: {
// Platform UUID.
platform_id: string
// Platform Model.
platform_model: structpb.#Struct
}

View File

@@ -170,15 +170,25 @@ func (c *Config) Finalize() error {
// Vet validates the config.
func (c *Config) Vet() error {
if c == nil || c.logConfig == nil {
return fmt.Errorf("cannot vet: not configured")
}
return c.logConfig.Vet()
}
// Logger returns a *slog.Logger configured by the user.
// Logger returns a *slog.Logger configured by the user or the default logger if
// no logger has been configured by the user.
func (c *Config) Logger() *slog.Logger {
if c == nil {
return slog.Default()
}
if c.logger != nil {
return c.logger
}
return c.logConfig.NewLogger(c.options.stderr)
if c.logConfig == nil {
return slog.Default()
}
return c.logConfig.NewLogger(c.Stderr())
}
// NewTopLevelLogger returns a *slog.Logger with a handler that filters source
@@ -189,21 +199,33 @@ func (c *Config) NewTopLevelLogger() *slog.Logger {
// Stdin should be used instead of os.Stdin to capture input from tests.
func (c *Config) Stdin() io.Reader {
if c == nil || c.options == nil {
return os.Stdin
}
return c.options.stdin
}
// Stdout should be used instead of os.Stdout to capture output for tests.
func (c *Config) Stdout() io.Writer {
if c == nil || c.options == nil {
return os.Stdout
}
return c.options.stdout
}
// Stderr should be used instead of os.Stderr to capture output for tests.
func (c *Config) Stderr() io.Writer {
if c == nil || c.options == nil {
return os.Stderr
}
return c.options.stderr
}
// WriteTo returns the write to path configured by flags.
func (c *Config) WriteTo() string {
if c == nil {
return ""
}
return c.writeTo
}
@@ -230,6 +252,9 @@ func (c *Config) Write(p []byte) {
// ClusterName returns the cluster name configured by flags.
func (c *Config) ClusterName() string {
if c == nil {
return ""
}
return c.clusterName
}
@@ -243,7 +268,7 @@ func (c *Config) KVKubeconfig() string {
// KVNamespace returns the configured namespace to operate against in the provisioner cluster.
func (c *Config) KVNamespace() string {
if c.kvNamespace == nil {
if c == nil || c.kvNamespace == nil {
return DefaultProvisionerNamespace
}
return *c.kvNamespace
@@ -251,7 +276,7 @@ func (c *Config) KVNamespace() string {
// TxtarIndex returns the
func (c *Config) TxtarIndex() int {
if c.txtarIndex == nil {
if c == nil || c.txtarIndex == nil {
return 0
}
return *c.txtarIndex
@@ -259,7 +284,7 @@ func (c *Config) TxtarIndex() int {
// ProvisionerClientset returns a kubernetes client set for the provisioner cluster.
func (c *Config) ProvisionerClientset() (kubernetes.Interface, error) {
if c.provisionerClientset == nil {
if c == nil || c.provisionerClientset == nil {
kcfg, err := clientcmd.BuildConfigFromFlags("", c.KVKubeconfig())
if err != nil {
return nil, errors.Wrap(err)

View File

@@ -5,29 +5,13 @@ import (
"bytes"
"context"
"encoding/json"
"os"
"path/filepath"
"github.com/gogo/protobuf/jsonpb"
"github.com/holos-run/holos/api/v1alpha1"
"github.com/holos-run/holos/internal/errors"
object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
)
// LoadPlatform loads the platform.metadata.json file from a named path.
func LoadPlatform(ctx context.Context, name string) (*platform.Platform, error) {
data, err := os.ReadFile(filepath.Join(name, "platform.metadata.json"))
if err != nil {
return nil, err
}
p := &platform.Platform{}
if err := jsonpb.Unmarshal(bytes.NewReader(data), p); err != nil {
return nil, err
}
return p, nil
}
// PlatformForm builds a json powered web form from CUE code. The CUE code is
// expected to be derived from the code generated by the `holos generate
// platform` command.

View File

@@ -0,0 +1,32 @@
package render
import (
"context"
"fmt"
"io"
"time"
"github.com/holos-run/holos/api/v1alpha1"
"github.com/holos-run/holos/internal/errors"
"github.com/holos-run/holos/internal/server/middleware/logger"
"github.com/holos-run/holos/internal/util"
)
func Platform(ctx context.Context, pf *v1alpha1.Platform, stderr io.Writer) error {
total := len(pf.Spec.Components)
for idx, component := range pf.Spec.Components {
start := time.Now()
log := logger.FromContext(ctx).With("path", component.Path, "cluster", component.Cluster, "num", idx+1, "total", total)
log.DebugContext(ctx, "render component")
// Execute a sub-process to limit CUE memory usage.
args := []string{"render", "component", "--cluster-name", component.Cluster, component.Path}
result, err := util.RunCmd(ctx, "holos", args...)
if err != nil {
io.Copy(stderr, result.Stderr)
return errors.Wrap(fmt.Errorf("could not render component: %w", err))
}
duration := time.Since(start)
log.InfoContext(ctx, "ok render component", "duration", duration)
}
return nil
}

View File

@@ -534,6 +534,65 @@ func (x *Form) GetFieldConfigs() []*structpb.Struct {
return nil
}
// PlatformConfig represents the data passed from the holos cli to CUE when
// rendering configuration.
type PlatformConfig struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Platform UUID.
PlatformId string `protobuf:"bytes,1,opt,name=platform_id,json=platformId,proto3" json:"platform_id,omitempty"`
// Platform Model.
PlatformModel *structpb.Struct `protobuf:"bytes,2,opt,name=platform_model,json=platformModel,proto3" json:"platform_model,omitempty"`
}
func (x *PlatformConfig) Reset() {
*x = PlatformConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_holos_object_v1alpha1_object_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PlatformConfig) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PlatformConfig) ProtoMessage() {}
func (x *PlatformConfig) ProtoReflect() protoreflect.Message {
mi := &file_holos_object_v1alpha1_object_proto_msgTypes[7]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PlatformConfig.ProtoReflect.Descriptor instead.
func (*PlatformConfig) Descriptor() ([]byte, []int) {
return file_holos_object_v1alpha1_object_proto_rawDescGZIP(), []int{7}
}
func (x *PlatformConfig) GetPlatformId() string {
if x != nil {
return x.PlatformId
}
return ""
}
func (x *PlatformConfig) GetPlatformModel() *structpb.Struct {
if x != nil {
return x.PlatformModel
}
return nil
}
var File_holos_object_v1alpha1_object_proto protoreflect.FileDescriptor
var file_holos_object_v1alpha1_object_proto_rawDesc = []byte{
@@ -619,12 +678,20 @@ var file_holos_object_v1alpha1_object_proto_rawDesc = []byte{
0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x73, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2d, 0x72, 0x75, 0x6e, 0x2f, 0x68, 0x6f,
0x6c, 0x6f, 0x73, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f,
0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x31, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
0x6e, 0x66, 0x69, 0x67, 0x73, 0x22, 0x7b, 0x0a, 0x0e, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72,
0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x29, 0x0a, 0x0b, 0x70, 0x6c, 0x61, 0x74, 0x66,
0x6f, 0x72, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48,
0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x0a, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d,
0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0e, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x6d,
0x6f, 0x64, 0x65, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
0x75, 0x63, 0x74, 0x52, 0x0d, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x4d, 0x6f, 0x64,
0x65, 0x6c, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x68, 0x6f, 0x6c, 0x6f, 0x73, 0x2d, 0x72, 0x75, 0x6e, 0x2f, 0x68, 0x6f, 0x6c, 0x6f, 0x73,
0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x68, 0x6f, 0x6c,
0x6f, 0x73, 0x2f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
0x61, 0x31, 0x3b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
@@ -639,7 +706,7 @@ func file_holos_object_v1alpha1_object_proto_rawDescGZIP() []byte {
return file_holos_object_v1alpha1_object_proto_rawDescData
}
var file_holos_object_v1alpha1_object_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
var file_holos_object_v1alpha1_object_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_holos_object_v1alpha1_object_proto_goTypes = []interface{}{
(*Detail)(nil), // 0: holos.object.v1alpha1.Detail
(*Subject)(nil), // 1: holos.object.v1alpha1.Subject
@@ -648,21 +715,23 @@ var file_holos_object_v1alpha1_object_proto_goTypes = []interface{}{
(*ResourceEditor)(nil), // 4: holos.object.v1alpha1.ResourceEditor
(*ResourceOwner)(nil), // 5: holos.object.v1alpha1.ResourceOwner
(*Form)(nil), // 6: holos.object.v1alpha1.Form
(*timestamppb.Timestamp)(nil), // 7: google.protobuf.Timestamp
(*structpb.Struct)(nil), // 8: google.protobuf.Struct
(*PlatformConfig)(nil), // 7: holos.object.v1alpha1.PlatformConfig
(*timestamppb.Timestamp)(nil), // 8: google.protobuf.Timestamp
(*structpb.Struct)(nil), // 9: google.protobuf.Struct
}
var file_holos_object_v1alpha1_object_proto_depIdxs = []int32{
4, // 0: holos.object.v1alpha1.Detail.created_by:type_name -> holos.object.v1alpha1.ResourceEditor
7, // 1: holos.object.v1alpha1.Detail.created_at:type_name -> google.protobuf.Timestamp
8, // 1: holos.object.v1alpha1.Detail.created_at:type_name -> google.protobuf.Timestamp
4, // 2: holos.object.v1alpha1.Detail.updated_by:type_name -> holos.object.v1alpha1.ResourceEditor
7, // 3: holos.object.v1alpha1.Detail.updated_at:type_name -> google.protobuf.Timestamp
8, // 3: holos.object.v1alpha1.Detail.updated_at:type_name -> google.protobuf.Timestamp
1, // 4: holos.object.v1alpha1.UserRef.subject:type_name -> holos.object.v1alpha1.Subject
8, // 5: holos.object.v1alpha1.Form.field_configs:type_name -> google.protobuf.Struct
6, // [6:6] is the sub-list for method output_type
6, // [6:6] is the sub-list for method input_type
6, // [6:6] is the sub-list for extension type_name
6, // [6:6] is the sub-list for extension extendee
0, // [0:6] is the sub-list for field type_name
9, // 5: holos.object.v1alpha1.Form.field_configs:type_name -> google.protobuf.Struct
9, // 6: holos.object.v1alpha1.PlatformConfig.platform_model:type_name -> google.protobuf.Struct
7, // [7:7] is the sub-list for method output_type
7, // [7:7] is the sub-list for method input_type
7, // [7:7] is the sub-list for extension type_name
7, // [7:7] is the sub-list for extension extendee
0, // [0:7] is the sub-list for field type_name
}
func init() { file_holos_object_v1alpha1_object_proto_init() }
@@ -755,6 +824,18 @@ func file_holos_object_v1alpha1_object_proto_init() {
return nil
}
}
file_holos_object_v1alpha1_object_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PlatformConfig); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
file_holos_object_v1alpha1_object_proto_msgTypes[0].OneofWrappers = []interface{}{}
file_holos_object_v1alpha1_object_proto_msgTypes[2].OneofWrappers = []interface{}{
@@ -779,7 +860,7 @@ func file_holos_object_v1alpha1_object_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_holos_object_v1alpha1_object_proto_rawDesc,
NumEnums: 0,
NumMessages: 7,
NumMessages: 8,
NumExtensions: 0,
NumServices: 0,
},

View File

@@ -87,3 +87,12 @@ message Form {
// organized by section.
repeated google.protobuf.Struct field_configs = 1;
}
// PlatformConfig represents the data passed from the holos cli to CUE when
// rendering configuration.
message PlatformConfig {
// Platform UUID.
string platform_id = 1 [(buf.validate.field).string.uuid = true];
// Platform Model.
google.protobuf.Struct platform_model = 2;
}

View File

@@ -1 +1 @@
0
2