mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 00:37:45 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bac7aec0ba | ||
|
|
42f916af41 | ||
|
|
47a5e237e0 | ||
|
|
1279e2351a | ||
|
|
adb8177026 | ||
|
|
4e8fa5abda | ||
|
|
6894f45b6c | ||
|
|
89d25be837 | ||
|
|
5b33e48552 | ||
|
|
79e8ab639a | ||
|
|
a0cc673736 | ||
|
|
d06ecfadc8 | ||
|
|
64a117b0c3 | ||
|
|
cf006be9cf | ||
|
|
45ad3d8e63 | ||
|
|
441c968c4f | ||
|
|
99f2763fdf | ||
|
|
1312395a11 | ||
|
|
615f147bcb |
2
Makefile
2
Makefile
@@ -87,6 +87,8 @@ test: ## Run tests.
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run linters.
|
||||
buf lint
|
||||
cd internal/frontend/holos && ng lint
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: coverage
|
||||
|
||||
@@ -39,3 +39,14 @@ func (bp *BuildPlan) Validate() error {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
9
api/v1alpha1/platform.go
Normal file
9
api/v1alpha1/platform.go
Normal file
@@ -0,0 +1,9 @@
|
||||
package v1alpha1
|
||||
|
||||
// Platform represents a platform to manage. A Platform resource tells holos
|
||||
// which components to build. The primary use case is to specify the cluster
|
||||
// names, cluster types, and holos components to build.
|
||||
type Platform struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
Metadata ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
}
|
||||
@@ -19,6 +19,14 @@ type Result struct {
|
||||
accumulatedOutput string
|
||||
}
|
||||
|
||||
// Continue returns true if Skip is true indicating the result is to be skipped over.
|
||||
func (r *Result) Continue() bool {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
return r.Skip
|
||||
}
|
||||
|
||||
func (r *Result) Name() string {
|
||||
return r.Metadata.Name
|
||||
}
|
||||
@@ -32,6 +40,11 @@ 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
|
||||
|
||||
@@ -8,3 +8,13 @@ type TypeMeta struct {
|
||||
func (tm *TypeMeta) GetKind() string {
|
||||
return tm.Kind
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetAPIVersion() string {
|
||||
return tm.Kind
|
||||
}
|
||||
|
||||
// Discriminator is an interface to discriminate the kind api object.
|
||||
type Discriminator interface {
|
||||
GetKind() string
|
||||
GetAPIVersion() string
|
||||
}
|
||||
|
||||
@@ -18,7 +18,3 @@ plugins:
|
||||
out: internal/frontend/holos/src/app/gen
|
||||
opt:
|
||||
- target=ts
|
||||
- plugin: connect-query
|
||||
out: internal/frontend/holos/src/app/gen
|
||||
opt:
|
||||
- target=ts
|
||||
|
||||
@@ -18,6 +18,7 @@ import "encoding/yaml"
|
||||
Issuer?: [Name=_]: #Issuer & {metadata: name: Name}
|
||||
Gateway?: [Name=_]: #Gateway & {metadata: name: Name}
|
||||
ConfigMap?: [Name=_]: #ConfigMap & {metadata: name: Name}
|
||||
ServiceAccount?: [Name=_]: #ServiceAccount & {metadata: name: Name}
|
||||
|
||||
Deployment?: [_]: #Deployment
|
||||
StatefulSet?: [_]: #StatefulSet
|
||||
|
||||
@@ -89,7 +89,8 @@ _IngressAuthProxy: {
|
||||
spec: {
|
||||
securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
containers: [{
|
||||
image: "quay.io/oauth2-proxy/oauth2-proxy:v7.6.0"
|
||||
// image: "quay.io/oauth3-proxy/oauth2-proxy:v7.6.0"
|
||||
image: "quay.io/holos/oauth2-proxy:v7.6.0-1-g77a03ae2"
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
name: "oauth2-proxy"
|
||||
volumeMounts: [{
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package holos
|
||||
|
||||
let Namespace = "dev-holos"
|
||||
let Holos = "holos"
|
||||
|
||||
// spec represents the output provided to holos
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
metadata: name: "dev-holos-app"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
|
||||
// OBJECTS represents the kubernetes api objects to manage.
|
||||
let OBJECTS = #APIObjects & {
|
||||
apiObjects: Deployment: holos: {
|
||||
metadata: {
|
||||
name: Holos
|
||||
namespace: Namespace
|
||||
labels: app: Holos
|
||||
}
|
||||
spec: {
|
||||
selector: matchLabels: app: Holos
|
||||
template: metadata: labels: {
|
||||
app: Holos
|
||||
"sidecar.istio.io/inject": "true"
|
||||
}
|
||||
strategy: rollingUpdate: maxSurge: 1
|
||||
strategy: rollingUpdate: maxUnavailable: 0
|
||||
template: {
|
||||
spec: {
|
||||
serviceAccountName: Holos
|
||||
securityContext: seccompProfile: type: "RuntimeDefault"
|
||||
containers: [
|
||||
{
|
||||
name: Holos
|
||||
image: "271053619184.dkr.ecr.us-east-2.amazonaws.com/holos-run/holos-server/holos:0.73.1"
|
||||
imagePullPolicy: "Always"
|
||||
env: [
|
||||
{
|
||||
name: "TZ"
|
||||
value: "America/Los_Angeles"
|
||||
},
|
||||
{
|
||||
name: "DATABASE_URL"
|
||||
valueFrom: secretKeyRef: {
|
||||
key: "uri"
|
||||
name: "holos-pguser-holos"
|
||||
}
|
||||
},
|
||||
]
|
||||
ports: [
|
||||
{
|
||||
containerPort: 3000
|
||||
name: "http"
|
||||
protocol: "TCP"
|
||||
},
|
||||
]
|
||||
securityContext: capabilities: drop: ["ALL"]
|
||||
securityContext: allowPrivilegeEscalation: false
|
||||
securityContext: runAsNonRoot: true
|
||||
resources: limits: {
|
||||
cpu: "0.25"
|
||||
memory: "256Mi"
|
||||
}
|
||||
resources: requests: resources.limits
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package holos
|
||||
|
||||
let Namespace = "dev-holos"
|
||||
let Holos = "holos"
|
||||
|
||||
// spec represents the output provided to holos
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
metadata: name: "dev-holos-infra"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
|
||||
let Metadata = {
|
||||
name: Holos
|
||||
namespace: Namespace
|
||||
labels: app: Holos
|
||||
}
|
||||
|
||||
// OBJECTS represents the kubernetes api objects to manage.
|
||||
let OBJECTS = #APIObjects & {
|
||||
// Postgres
|
||||
// Deployment
|
||||
// VirtualService
|
||||
|
||||
apiObjects: ServiceAccount: holos: {
|
||||
metadata: Metadata
|
||||
imagePullSecrets: [{name: "kube-system-ecr-image-pull-creds"}]
|
||||
}
|
||||
|
||||
apiObjects: PostgresCluster: holos: {
|
||||
apiVersion: "postgres-operator.crunchydata.com/v1beta1"
|
||||
metadata: Metadata
|
||||
spec: {
|
||||
image: "registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.1-0"
|
||||
instances: [{
|
||||
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: [{
|
||||
podAffinityTerm: {
|
||||
labelSelector: matchLabels: "postgres-operator.crunchydata.com/cluster": "holos"
|
||||
topologyKey: "kubernetes.io/hostname"
|
||||
}
|
||||
weight: 1
|
||||
}]
|
||||
dataVolumeClaimSpec: {
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources: requests: storage: "1Gi"
|
||||
}
|
||||
name: "db"
|
||||
replicas: 1
|
||||
}]
|
||||
port: 5432
|
||||
postgresVersion: 16
|
||||
users: [{
|
||||
databases: ["holos"]
|
||||
name: "holos"
|
||||
options: "SUPERUSER"
|
||||
}]
|
||||
backups: pgbackrest: {
|
||||
global: {
|
||||
"archive-async": "y"
|
||||
"archive-push-queue-max": "100MiB"
|
||||
"spool-path": "/pgdata/backups"
|
||||
}
|
||||
image: "registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.47-2"
|
||||
repos: [{
|
||||
name: "repo1"
|
||||
volume: volumeClaimSpec: {
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources: requests: storage: "1Gi"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apiObjects: Service: holos: {
|
||||
apiVersion: "v1"
|
||||
metadata: Metadata
|
||||
spec: {
|
||||
type: "ClusterIP"
|
||||
selector: app: "holos"
|
||||
ports: [{
|
||||
appProtocol: "http2"
|
||||
name: "http"
|
||||
port: 3000
|
||||
protocol: "TCP"
|
||||
targetPort: 3000
|
||||
}, {
|
||||
appProtocol: "http"
|
||||
name: "metrics"
|
||||
port: 9090
|
||||
protocol: "TCP"
|
||||
targetPort: 9090
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
apiObjects: VirtualService: holos: {
|
||||
apiVersion: "networking.istio.io/v1beta1"
|
||||
metadata: Metadata
|
||||
spec: {
|
||||
gateways: ["istio-ingress/default"]
|
||||
hosts: [
|
||||
"app.dev.holos.run",
|
||||
"app.dev.\(#ClusterName).holos.run",
|
||||
]
|
||||
http: [{
|
||||
match: [{
|
||||
uri: prefix: "/ui"
|
||||
}]
|
||||
name: "ui"
|
||||
route: [{
|
||||
destination: {
|
||||
host: "holos"
|
||||
port: number: 3000
|
||||
}
|
||||
}]
|
||||
}, {
|
||||
name: "api"
|
||||
route: [{
|
||||
destination: {
|
||||
host: "holos"
|
||||
port: number: 3000
|
||||
}
|
||||
}]
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
16
hack/setup/bare
Executable file
16
hack/setup/bare
Executable file
@@ -0,0 +1,16 @@
|
||||
#! /bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
TOPLEVEL="$(cd $(dirname "$0") && git rev-parse --show-toplevel)"
|
||||
|
||||
host="jeff.app.dev.k2.holos.run:443"
|
||||
|
||||
read -p "Reset all data in $host? " choice
|
||||
case "$choice" in
|
||||
y|Y) echo "proceeding...";;
|
||||
*) exit 1;;
|
||||
esac
|
||||
|
||||
|
||||
grpcurl -H "x-oidc-id-token: $(holos token)" $host holos.v1alpha1.SystemService.DropTables
|
||||
grpcurl -H "x-oidc-id-token: $(holos token)" $host holos.v1alpha1.SystemService.SeedDatabase
|
||||
@@ -3,5 +3,6 @@ USER root
|
||||
WORKDIR /app
|
||||
ADD bin bin
|
||||
RUN chown -R app: /app
|
||||
USER app
|
||||
# Kubernetes requires the user to be numeric
|
||||
USER 8192
|
||||
ENTRYPOINT bin/holos server
|
||||
|
||||
273
internal/builder/builder.go
Normal file
273
internal/builder/builder.go
Normal file
@@ -0,0 +1,273 @@
|
||||
// Package builder is responsible for building fully rendered kubernetes api
|
||||
// objects from various input directories. A directory may contain a platform
|
||||
// spec or a component spec.
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue/build"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
KubernetesObjects = v1alpha1.KubernetesObjectsKind
|
||||
// Helm is the value of the kind field of holos build output indicating helm
|
||||
// values and helm command information.
|
||||
Helm = v1alpha1.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 = v1alpha1.KustomizeBuildKind
|
||||
)
|
||||
|
||||
// An Option configures a Builder
|
||||
type Option func(*config)
|
||||
|
||||
type config struct {
|
||||
args []string
|
||||
cluster string
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
cfg config
|
||||
}
|
||||
|
||||
// New returns a new *Builder configured by opts Option.
|
||||
func New(opts ...Option) *Builder {
|
||||
var cfg config
|
||||
for _, f := range opts {
|
||||
f(&cfg)
|
||||
}
|
||||
b := &Builder{cfg: cfg}
|
||||
return b
|
||||
}
|
||||
|
||||
// Entrypoints configures the leaf directories Builder builds.
|
||||
func Entrypoints(args []string) Option {
|
||||
return func(cfg *config) { cfg.args = args }
|
||||
}
|
||||
|
||||
// Cluster configures the cluster name for the holos component instance.
|
||||
func Cluster(name string) Option {
|
||||
return func(cfg *config) { cfg.cluster = name }
|
||||
}
|
||||
|
||||
// Cluster returns the cluster name of the component instance being built.
|
||||
func (b *Builder) Cluster() string {
|
||||
return b.cfg.cluster
|
||||
}
|
||||
|
||||
// Instances returns the cue build instances being built.
|
||||
func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
mod, err := b.findCueMod()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
dir := string(mod)
|
||||
|
||||
cfg := load.Config{Dir: dir}
|
||||
|
||||
// Make args relative to the module directory
|
||||
args := make([]string, len(b.cfg.args))
|
||||
for idx, path := range b.cfg.args {
|
||||
target, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not find absolute path: %w", err))
|
||||
}
|
||||
relPath, err := filepath.Rel(dir, target)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("invalid argument, must be relative to cue.mod: %w", err))
|
||||
}
|
||||
relPath = "./" + relPath
|
||||
args[idx] = relPath
|
||||
equiv := fmt.Sprintf("cue export --out yaml -t cluster=%v %v", b.Cluster(), relPath)
|
||||
log.Debug("cue: equivalent command: " + equiv)
|
||||
}
|
||||
|
||||
// Refer to https://github.com/cue-lang/cue/blob/v0.7.0/cmd/cue/cmd/common.go#L429
|
||||
cfg.Tags = append(cfg.Tags, "cluster="+b.Cluster())
|
||||
log.DebugContext(ctx, fmt.Sprintf("cue: tags %v", cfg.Tags))
|
||||
|
||||
return load.Instances(args, &cfg), nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context) (results []*v1alpha1.Result, err error) {
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "cue: building instances")
|
||||
instances, err := b.Instances(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = make([]*v1alpha1.Result, 0, len(instances)*8)
|
||||
|
||||
// Each CUE instance provides a BuildPlan
|
||||
for idx, instance := range instances {
|
||||
log.DebugContext(ctx, "cue: building instance", "idx", idx, "dir", instance.Dir)
|
||||
r, err := b.runInstance(ctx, instance)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not run: %w", err))
|
||||
}
|
||||
results = append(results, r...)
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (results []*v1alpha1.Result, err 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 build plan")
|
||||
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 BuildPlan: %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()
|
||||
|
||||
switch tm.Kind {
|
||||
case "BuildPlan":
|
||||
var bp v1alpha1.BuildPlan
|
||||
if err = decoder.Decode(&bp); err != nil {
|
||||
err = errors.Wrap(fmt.Errorf("could not decode BuildPlan %s: %w", instance.Dir, err))
|
||||
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))
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
if err := buildPlan.Validate(); err != nil {
|
||||
log.WarnContext(ctx, "could not validate", "skipped", true, "err", err)
|
||||
return nil, errors.Wrap(fmt.Errorf("could not validate %w", err))
|
||||
}
|
||||
|
||||
if buildPlan.Spec.Disabled {
|
||||
log.DebugContext(ctx, "skipped: spec.disabled is true", "skipped", true)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: concurrent renders
|
||||
results = make([]*v1alpha1.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 {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
}
|
||||
}
|
||||
|
||||
for _, component := range buildPlan.Spec.Components.KubernetesObjectsList {
|
||||
if result, err := component.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 {
|
||||
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 {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
}
|
||||
}
|
||||
|
||||
log.DebugContext(ctx, "returning results", "len", len(results))
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// findCueMod returns the root module location containing the cue.mod file or
|
||||
// directory or an error if the builder arguments do not share a common root
|
||||
// module.
|
||||
func (b *Builder) findCueMod() (dir holos.PathCueMod, err error) {
|
||||
for _, origPath := range b.cfg.args {
|
||||
absPath, err := filepath.Abs(origPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := holos.PathCueMod(absPath)
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(string(path), "cue.mod")); err == nil {
|
||||
if dir != "" && dir != path {
|
||||
return "", fmt.Errorf("multiple modules not supported: %v is not %v", dir, path)
|
||||
}
|
||||
dir = path
|
||||
break
|
||||
} else if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
parentPath := holos.PathCueMod(filepath.Dir(string(path)))
|
||||
if parentPath == path {
|
||||
return "", fmt.Errorf("no cue.mod from root to leaf: %v", origPath)
|
||||
}
|
||||
path = parentPath
|
||||
}
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
@@ -2,12 +2,13 @@ package build
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/holos-run/holos/internal/builder"
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/internal/builder"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -20,10 +21,12 @@ func makeBuildRunFunc(cfg *holos.Config) command.RunFunc {
|
||||
return err
|
||||
}
|
||||
outs := make([]string, 0, len(results))
|
||||
for _, result := range results {
|
||||
if result.Skip {
|
||||
for idx, result := range results {
|
||||
if result == nil || result.Skip {
|
||||
slog.Debug("skip result", "idx", idx, "result", result)
|
||||
continue
|
||||
}
|
||||
slog.Debug("append result", "idx", idx, "result.kind", result.Kind)
|
||||
outs = append(outs, result.AccumulatedOutput())
|
||||
}
|
||||
out := strings.Join(outs, "---\n")
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package render
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"github.com/holos-run/holos/internal/builder"
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/internal/builder"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -53,8 +54,9 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
// 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.
|
||||
for _, result := range results {
|
||||
if result.Skip {
|
||||
var result Result
|
||||
for _, result = range results {
|
||||
if result.Continue() {
|
||||
continue
|
||||
}
|
||||
// API Objects
|
||||
@@ -64,7 +66,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
}
|
||||
// Kustomization
|
||||
path = result.KustomizationFilename(cfg.WriteTo(), cfg.ClusterName())
|
||||
if err := result.Save(ctx, path, result.KsContent); err != nil {
|
||||
if err := result.Save(ctx, path, result.KustomizationContent()); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.InfoContext(ctx, "rendered "+result.Name(), "status", "ok", "action", "rendered", "name", result.Name())
|
||||
@@ -73,3 +75,13 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
type Result interface {
|
||||
Continue() bool
|
||||
Name() string
|
||||
Filename(writeTo string, cluster string) string
|
||||
KustomizationFilename(writeTo string, cluster string) string
|
||||
Save(ctx context.Context, path string, content string) error
|
||||
AccumulatedOutput() string
|
||||
KustomizationContent() string
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ var (
|
||||
{Name: "updated_at", Type: field.TypeTime},
|
||||
{Name: "name", Type: field.TypeString},
|
||||
{Name: "display_name", Type: field.TypeString},
|
||||
{Name: "config_form", Type: field.TypeBytes, Nullable: true},
|
||||
{Name: "config_values", Type: field.TypeBytes, Nullable: true},
|
||||
{Name: "config_cue", Type: field.TypeBytes, Nullable: true},
|
||||
{Name: "config_definition", Type: field.TypeString, Nullable: true},
|
||||
{Name: "form", Type: field.TypeJSON, Nullable: true},
|
||||
{Name: "model", Type: field.TypeJSON, Nullable: true},
|
||||
{Name: "cue", Type: field.TypeBytes, Nullable: true},
|
||||
{Name: "cue_definition", Type: field.TypeString, Nullable: true},
|
||||
{Name: "creator_id", Type: field.TypeUUID},
|
||||
{Name: "org_id", Type: field.TypeUUID},
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -812,10 +813,10 @@ type PlatformMutation struct {
|
||||
updated_at *time.Time
|
||||
name *string
|
||||
display_name *string
|
||||
config_form *[]byte
|
||||
config_values *[]byte
|
||||
config_cue *[]byte
|
||||
config_definition *string
|
||||
form **holos.Form
|
||||
model **holos.Model
|
||||
cue *[]byte
|
||||
cue_definition *string
|
||||
clearedFields map[string]struct{}
|
||||
creator *uuid.UUID
|
||||
clearedcreator bool
|
||||
@@ -1146,200 +1147,200 @@ func (m *PlatformMutation) ResetCreatorID() {
|
||||
m.creator = nil
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (m *PlatformMutation) SetConfigForm(b []byte) {
|
||||
m.config_form = &b
|
||||
// SetForm sets the "form" field.
|
||||
func (m *PlatformMutation) SetForm(h *holos.Form) {
|
||||
m.form = &h
|
||||
}
|
||||
|
||||
// ConfigForm returns the value of the "config_form" field in the mutation.
|
||||
func (m *PlatformMutation) ConfigForm() (r []byte, exists bool) {
|
||||
v := m.config_form
|
||||
// Form returns the value of the "form" field in the mutation.
|
||||
func (m *PlatformMutation) Form() (r *holos.Form, exists bool) {
|
||||
v := m.form
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldConfigForm returns the old "config_form" field's value of the Platform entity.
|
||||
// OldForm returns the old "form" field's value of the Platform entity.
|
||||
// If the Platform object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PlatformMutation) OldConfigForm(ctx context.Context) (v []byte, err error) {
|
||||
func (m *PlatformMutation) OldForm(ctx context.Context) (v *holos.Form, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldConfigForm is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldForm is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldConfigForm requires an ID field in the mutation")
|
||||
return v, errors.New("OldForm requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldConfigForm: %w", err)
|
||||
return v, fmt.Errorf("querying old value for OldForm: %w", err)
|
||||
}
|
||||
return oldValue.ConfigForm, nil
|
||||
return oldValue.Form, nil
|
||||
}
|
||||
|
||||
// ClearConfigForm clears the value of the "config_form" field.
|
||||
func (m *PlatformMutation) ClearConfigForm() {
|
||||
m.config_form = nil
|
||||
m.clearedFields[platform.FieldConfigForm] = struct{}{}
|
||||
// ClearForm clears the value of the "form" field.
|
||||
func (m *PlatformMutation) ClearForm() {
|
||||
m.form = nil
|
||||
m.clearedFields[platform.FieldForm] = struct{}{}
|
||||
}
|
||||
|
||||
// ConfigFormCleared returns if the "config_form" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) ConfigFormCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldConfigForm]
|
||||
// FormCleared returns if the "form" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) FormCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldForm]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ResetConfigForm resets all changes to the "config_form" field.
|
||||
func (m *PlatformMutation) ResetConfigForm() {
|
||||
m.config_form = nil
|
||||
delete(m.clearedFields, platform.FieldConfigForm)
|
||||
// ResetForm resets all changes to the "form" field.
|
||||
func (m *PlatformMutation) ResetForm() {
|
||||
m.form = nil
|
||||
delete(m.clearedFields, platform.FieldForm)
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (m *PlatformMutation) SetConfigValues(b []byte) {
|
||||
m.config_values = &b
|
||||
// SetModel sets the "model" field.
|
||||
func (m *PlatformMutation) SetModel(h *holos.Model) {
|
||||
m.model = &h
|
||||
}
|
||||
|
||||
// ConfigValues returns the value of the "config_values" field in the mutation.
|
||||
func (m *PlatformMutation) ConfigValues() (r []byte, exists bool) {
|
||||
v := m.config_values
|
||||
// Model returns the value of the "model" field in the mutation.
|
||||
func (m *PlatformMutation) Model() (r *holos.Model, exists bool) {
|
||||
v := m.model
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldConfigValues returns the old "config_values" field's value of the Platform entity.
|
||||
// OldModel returns the old "model" field's value of the Platform entity.
|
||||
// If the Platform object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PlatformMutation) OldConfigValues(ctx context.Context) (v []byte, err error) {
|
||||
func (m *PlatformMutation) OldModel(ctx context.Context) (v *holos.Model, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldConfigValues is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldModel is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldConfigValues requires an ID field in the mutation")
|
||||
return v, errors.New("OldModel requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldConfigValues: %w", err)
|
||||
return v, fmt.Errorf("querying old value for OldModel: %w", err)
|
||||
}
|
||||
return oldValue.ConfigValues, nil
|
||||
return oldValue.Model, nil
|
||||
}
|
||||
|
||||
// ClearConfigValues clears the value of the "config_values" field.
|
||||
func (m *PlatformMutation) ClearConfigValues() {
|
||||
m.config_values = nil
|
||||
m.clearedFields[platform.FieldConfigValues] = struct{}{}
|
||||
// ClearModel clears the value of the "model" field.
|
||||
func (m *PlatformMutation) ClearModel() {
|
||||
m.model = nil
|
||||
m.clearedFields[platform.FieldModel] = struct{}{}
|
||||
}
|
||||
|
||||
// ConfigValuesCleared returns if the "config_values" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) ConfigValuesCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldConfigValues]
|
||||
// ModelCleared returns if the "model" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) ModelCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldModel]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ResetConfigValues resets all changes to the "config_values" field.
|
||||
func (m *PlatformMutation) ResetConfigValues() {
|
||||
m.config_values = nil
|
||||
delete(m.clearedFields, platform.FieldConfigValues)
|
||||
// ResetModel resets all changes to the "model" field.
|
||||
func (m *PlatformMutation) ResetModel() {
|
||||
m.model = nil
|
||||
delete(m.clearedFields, platform.FieldModel)
|
||||
}
|
||||
|
||||
// SetConfigCue sets the "config_cue" field.
|
||||
func (m *PlatformMutation) SetConfigCue(b []byte) {
|
||||
m.config_cue = &b
|
||||
// SetCue sets the "cue" field.
|
||||
func (m *PlatformMutation) SetCue(b []byte) {
|
||||
m.cue = &b
|
||||
}
|
||||
|
||||
// ConfigCue returns the value of the "config_cue" field in the mutation.
|
||||
func (m *PlatformMutation) ConfigCue() (r []byte, exists bool) {
|
||||
v := m.config_cue
|
||||
// Cue returns the value of the "cue" field in the mutation.
|
||||
func (m *PlatformMutation) Cue() (r []byte, exists bool) {
|
||||
v := m.cue
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldConfigCue returns the old "config_cue" field's value of the Platform entity.
|
||||
// OldCue returns the old "cue" field's value of the Platform entity.
|
||||
// If the Platform object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PlatformMutation) OldConfigCue(ctx context.Context) (v []byte, err error) {
|
||||
func (m *PlatformMutation) OldCue(ctx context.Context) (v []byte, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldConfigCue is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldCue is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldConfigCue requires an ID field in the mutation")
|
||||
return v, errors.New("OldCue requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldConfigCue: %w", err)
|
||||
return v, fmt.Errorf("querying old value for OldCue: %w", err)
|
||||
}
|
||||
return oldValue.ConfigCue, nil
|
||||
return oldValue.Cue, nil
|
||||
}
|
||||
|
||||
// ClearConfigCue clears the value of the "config_cue" field.
|
||||
func (m *PlatformMutation) ClearConfigCue() {
|
||||
m.config_cue = nil
|
||||
m.clearedFields[platform.FieldConfigCue] = struct{}{}
|
||||
// ClearCue clears the value of the "cue" field.
|
||||
func (m *PlatformMutation) ClearCue() {
|
||||
m.cue = nil
|
||||
m.clearedFields[platform.FieldCue] = struct{}{}
|
||||
}
|
||||
|
||||
// ConfigCueCleared returns if the "config_cue" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) ConfigCueCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldConfigCue]
|
||||
// CueCleared returns if the "cue" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) CueCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldCue]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ResetConfigCue resets all changes to the "config_cue" field.
|
||||
func (m *PlatformMutation) ResetConfigCue() {
|
||||
m.config_cue = nil
|
||||
delete(m.clearedFields, platform.FieldConfigCue)
|
||||
// ResetCue resets all changes to the "cue" field.
|
||||
func (m *PlatformMutation) ResetCue() {
|
||||
m.cue = nil
|
||||
delete(m.clearedFields, platform.FieldCue)
|
||||
}
|
||||
|
||||
// SetConfigDefinition sets the "config_definition" field.
|
||||
func (m *PlatformMutation) SetConfigDefinition(s string) {
|
||||
m.config_definition = &s
|
||||
// SetCueDefinition sets the "cue_definition" field.
|
||||
func (m *PlatformMutation) SetCueDefinition(s string) {
|
||||
m.cue_definition = &s
|
||||
}
|
||||
|
||||
// ConfigDefinition returns the value of the "config_definition" field in the mutation.
|
||||
func (m *PlatformMutation) ConfigDefinition() (r string, exists bool) {
|
||||
v := m.config_definition
|
||||
// CueDefinition returns the value of the "cue_definition" field in the mutation.
|
||||
func (m *PlatformMutation) CueDefinition() (r string, exists bool) {
|
||||
v := m.cue_definition
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
return *v, true
|
||||
}
|
||||
|
||||
// OldConfigDefinition returns the old "config_definition" field's value of the Platform entity.
|
||||
// OldCueDefinition returns the old "cue_definition" field's value of the Platform entity.
|
||||
// If the Platform object wasn't provided to the builder, the object is fetched from the database.
|
||||
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
|
||||
func (m *PlatformMutation) OldConfigDefinition(ctx context.Context) (v string, err error) {
|
||||
func (m *PlatformMutation) OldCueDefinition(ctx context.Context) (v string, err error) {
|
||||
if !m.op.Is(OpUpdateOne) {
|
||||
return v, errors.New("OldConfigDefinition is only allowed on UpdateOne operations")
|
||||
return v, errors.New("OldCueDefinition is only allowed on UpdateOne operations")
|
||||
}
|
||||
if m.id == nil || m.oldValue == nil {
|
||||
return v, errors.New("OldConfigDefinition requires an ID field in the mutation")
|
||||
return v, errors.New("OldCueDefinition requires an ID field in the mutation")
|
||||
}
|
||||
oldValue, err := m.oldValue(ctx)
|
||||
if err != nil {
|
||||
return v, fmt.Errorf("querying old value for OldConfigDefinition: %w", err)
|
||||
return v, fmt.Errorf("querying old value for OldCueDefinition: %w", err)
|
||||
}
|
||||
return oldValue.ConfigDefinition, nil
|
||||
return oldValue.CueDefinition, nil
|
||||
}
|
||||
|
||||
// ClearConfigDefinition clears the value of the "config_definition" field.
|
||||
func (m *PlatformMutation) ClearConfigDefinition() {
|
||||
m.config_definition = nil
|
||||
m.clearedFields[platform.FieldConfigDefinition] = struct{}{}
|
||||
// ClearCueDefinition clears the value of the "cue_definition" field.
|
||||
func (m *PlatformMutation) ClearCueDefinition() {
|
||||
m.cue_definition = nil
|
||||
m.clearedFields[platform.FieldCueDefinition] = struct{}{}
|
||||
}
|
||||
|
||||
// ConfigDefinitionCleared returns if the "config_definition" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) ConfigDefinitionCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldConfigDefinition]
|
||||
// CueDefinitionCleared returns if the "cue_definition" field was cleared in this mutation.
|
||||
func (m *PlatformMutation) CueDefinitionCleared() bool {
|
||||
_, ok := m.clearedFields[platform.FieldCueDefinition]
|
||||
return ok
|
||||
}
|
||||
|
||||
// ResetConfigDefinition resets all changes to the "config_definition" field.
|
||||
func (m *PlatformMutation) ResetConfigDefinition() {
|
||||
m.config_definition = nil
|
||||
delete(m.clearedFields, platform.FieldConfigDefinition)
|
||||
// ResetCueDefinition resets all changes to the "cue_definition" field.
|
||||
func (m *PlatformMutation) ResetCueDefinition() {
|
||||
m.cue_definition = nil
|
||||
delete(m.clearedFields, platform.FieldCueDefinition)
|
||||
}
|
||||
|
||||
// ClearCreator clears the "creator" edge to the User entity.
|
||||
@@ -1462,17 +1463,17 @@ func (m *PlatformMutation) Fields() []string {
|
||||
if m.creator != nil {
|
||||
fields = append(fields, platform.FieldCreatorID)
|
||||
}
|
||||
if m.config_form != nil {
|
||||
fields = append(fields, platform.FieldConfigForm)
|
||||
if m.form != nil {
|
||||
fields = append(fields, platform.FieldForm)
|
||||
}
|
||||
if m.config_values != nil {
|
||||
fields = append(fields, platform.FieldConfigValues)
|
||||
if m.model != nil {
|
||||
fields = append(fields, platform.FieldModel)
|
||||
}
|
||||
if m.config_cue != nil {
|
||||
fields = append(fields, platform.FieldConfigCue)
|
||||
if m.cue != nil {
|
||||
fields = append(fields, platform.FieldCue)
|
||||
}
|
||||
if m.config_definition != nil {
|
||||
fields = append(fields, platform.FieldConfigDefinition)
|
||||
if m.cue_definition != nil {
|
||||
fields = append(fields, platform.FieldCueDefinition)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
@@ -1494,14 +1495,14 @@ func (m *PlatformMutation) Field(name string) (ent.Value, bool) {
|
||||
return m.DisplayName()
|
||||
case platform.FieldCreatorID:
|
||||
return m.CreatorID()
|
||||
case platform.FieldConfigForm:
|
||||
return m.ConfigForm()
|
||||
case platform.FieldConfigValues:
|
||||
return m.ConfigValues()
|
||||
case platform.FieldConfigCue:
|
||||
return m.ConfigCue()
|
||||
case platform.FieldConfigDefinition:
|
||||
return m.ConfigDefinition()
|
||||
case platform.FieldForm:
|
||||
return m.Form()
|
||||
case platform.FieldModel:
|
||||
return m.Model()
|
||||
case platform.FieldCue:
|
||||
return m.Cue()
|
||||
case platform.FieldCueDefinition:
|
||||
return m.CueDefinition()
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
@@ -1523,14 +1524,14 @@ func (m *PlatformMutation) OldField(ctx context.Context, name string) (ent.Value
|
||||
return m.OldDisplayName(ctx)
|
||||
case platform.FieldCreatorID:
|
||||
return m.OldCreatorID(ctx)
|
||||
case platform.FieldConfigForm:
|
||||
return m.OldConfigForm(ctx)
|
||||
case platform.FieldConfigValues:
|
||||
return m.OldConfigValues(ctx)
|
||||
case platform.FieldConfigCue:
|
||||
return m.OldConfigCue(ctx)
|
||||
case platform.FieldConfigDefinition:
|
||||
return m.OldConfigDefinition(ctx)
|
||||
case platform.FieldForm:
|
||||
return m.OldForm(ctx)
|
||||
case platform.FieldModel:
|
||||
return m.OldModel(ctx)
|
||||
case platform.FieldCue:
|
||||
return m.OldCue(ctx)
|
||||
case platform.FieldCueDefinition:
|
||||
return m.OldCueDefinition(ctx)
|
||||
}
|
||||
return nil, fmt.Errorf("unknown Platform field %s", name)
|
||||
}
|
||||
@@ -1582,33 +1583,33 @@ func (m *PlatformMutation) SetField(name string, value ent.Value) error {
|
||||
}
|
||||
m.SetCreatorID(v)
|
||||
return nil
|
||||
case platform.FieldConfigForm:
|
||||
case platform.FieldForm:
|
||||
v, ok := value.(*holos.Form)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetForm(v)
|
||||
return nil
|
||||
case platform.FieldModel:
|
||||
v, ok := value.(*holos.Model)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetModel(v)
|
||||
return nil
|
||||
case platform.FieldCue:
|
||||
v, ok := value.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetConfigForm(v)
|
||||
m.SetCue(v)
|
||||
return nil
|
||||
case platform.FieldConfigValues:
|
||||
v, ok := value.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetConfigValues(v)
|
||||
return nil
|
||||
case platform.FieldConfigCue:
|
||||
v, ok := value.([]byte)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetConfigCue(v)
|
||||
return nil
|
||||
case platform.FieldConfigDefinition:
|
||||
case platform.FieldCueDefinition:
|
||||
v, ok := value.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected type %T for field %s", value, name)
|
||||
}
|
||||
m.SetConfigDefinition(v)
|
||||
m.SetCueDefinition(v)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Platform field %s", name)
|
||||
@@ -1640,17 +1641,17 @@ func (m *PlatformMutation) AddField(name string, value ent.Value) error {
|
||||
// mutation.
|
||||
func (m *PlatformMutation) ClearedFields() []string {
|
||||
var fields []string
|
||||
if m.FieldCleared(platform.FieldConfigForm) {
|
||||
fields = append(fields, platform.FieldConfigForm)
|
||||
if m.FieldCleared(platform.FieldForm) {
|
||||
fields = append(fields, platform.FieldForm)
|
||||
}
|
||||
if m.FieldCleared(platform.FieldConfigValues) {
|
||||
fields = append(fields, platform.FieldConfigValues)
|
||||
if m.FieldCleared(platform.FieldModel) {
|
||||
fields = append(fields, platform.FieldModel)
|
||||
}
|
||||
if m.FieldCleared(platform.FieldConfigCue) {
|
||||
fields = append(fields, platform.FieldConfigCue)
|
||||
if m.FieldCleared(platform.FieldCue) {
|
||||
fields = append(fields, platform.FieldCue)
|
||||
}
|
||||
if m.FieldCleared(platform.FieldConfigDefinition) {
|
||||
fields = append(fields, platform.FieldConfigDefinition)
|
||||
if m.FieldCleared(platform.FieldCueDefinition) {
|
||||
fields = append(fields, platform.FieldCueDefinition)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
@@ -1666,17 +1667,17 @@ func (m *PlatformMutation) FieldCleared(name string) bool {
|
||||
// error if the field is not defined in the schema.
|
||||
func (m *PlatformMutation) ClearField(name string) error {
|
||||
switch name {
|
||||
case platform.FieldConfigForm:
|
||||
m.ClearConfigForm()
|
||||
case platform.FieldForm:
|
||||
m.ClearForm()
|
||||
return nil
|
||||
case platform.FieldConfigValues:
|
||||
m.ClearConfigValues()
|
||||
case platform.FieldModel:
|
||||
m.ClearModel()
|
||||
return nil
|
||||
case platform.FieldConfigCue:
|
||||
m.ClearConfigCue()
|
||||
case platform.FieldCue:
|
||||
m.ClearCue()
|
||||
return nil
|
||||
case platform.FieldConfigDefinition:
|
||||
m.ClearConfigDefinition()
|
||||
case platform.FieldCueDefinition:
|
||||
m.ClearCueDefinition()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Platform nullable field %s", name)
|
||||
@@ -1704,17 +1705,17 @@ func (m *PlatformMutation) ResetField(name string) error {
|
||||
case platform.FieldCreatorID:
|
||||
m.ResetCreatorID()
|
||||
return nil
|
||||
case platform.FieldConfigForm:
|
||||
m.ResetConfigForm()
|
||||
case platform.FieldForm:
|
||||
m.ResetForm()
|
||||
return nil
|
||||
case platform.FieldConfigValues:
|
||||
m.ResetConfigValues()
|
||||
case platform.FieldModel:
|
||||
m.ResetModel()
|
||||
return nil
|
||||
case platform.FieldConfigCue:
|
||||
m.ResetConfigCue()
|
||||
case platform.FieldCue:
|
||||
m.ResetCue()
|
||||
return nil
|
||||
case platform.FieldConfigDefinition:
|
||||
m.ResetConfigDefinition()
|
||||
case platform.FieldCueDefinition:
|
||||
m.ResetCueDefinition()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("unknown Platform field %s", name)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package ent
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
// Platform is the model entity for the Platform schema.
|
||||
@@ -32,14 +34,14 @@ type Platform struct {
|
||||
DisplayName string `json:"display_name,omitempty"`
|
||||
// CreatorID holds the value of the "creator_id" field.
|
||||
CreatorID uuid.UUID `json:"creator_id,omitempty"`
|
||||
// Opaque JSON bytes representing the platform config form.
|
||||
ConfigForm []byte `json:"config_form,omitempty"`
|
||||
// Opaque JSON bytes representing the platform config values.
|
||||
ConfigValues []byte `json:"config_values,omitempty"`
|
||||
// Opaque bytes representing the CUE definition of the config struct.
|
||||
ConfigCue []byte `json:"config_cue,omitempty"`
|
||||
// JSON representation of FormlyFormConfig[] refer to https://github.com/holos-run/holos/issues/161
|
||||
Form *holos.Form `json:"form,omitempty"`
|
||||
// JSON representation of the form model which holds user input values refer to https://github.com/holos-run/holos/issues/161
|
||||
Model *holos.Model `json:"model,omitempty"`
|
||||
// CUE definition to vet the model against e.g. #PlatformConfig
|
||||
Cue []byte `json:"cue,omitempty"`
|
||||
// The definition name to vet config_values against config_cue e.g. '#PlatformSpec'
|
||||
ConfigDefinition string `json:"config_definition,omitempty"`
|
||||
CueDefinition string `json:"cue_definition,omitempty"`
|
||||
// Edges holds the relations/edges for other nodes in the graph.
|
||||
// The values are being populated by the PlatformQuery when eager-loading is set.
|
||||
Edges PlatformEdges `json:"edges"`
|
||||
@@ -84,9 +86,9 @@ func (*Platform) scanValues(columns []string) ([]any, error) {
|
||||
values := make([]any, len(columns))
|
||||
for i := range columns {
|
||||
switch columns[i] {
|
||||
case platform.FieldConfigForm, platform.FieldConfigValues, platform.FieldConfigCue:
|
||||
case platform.FieldForm, platform.FieldModel, platform.FieldCue:
|
||||
values[i] = new([]byte)
|
||||
case platform.FieldName, platform.FieldDisplayName, platform.FieldConfigDefinition:
|
||||
case platform.FieldName, platform.FieldDisplayName, platform.FieldCueDefinition:
|
||||
values[i] = new(sql.NullString)
|
||||
case platform.FieldCreatedAt, platform.FieldUpdatedAt:
|
||||
values[i] = new(sql.NullTime)
|
||||
@@ -149,29 +151,33 @@ func (pl *Platform) assignValues(columns []string, values []any) error {
|
||||
} else if value != nil {
|
||||
pl.CreatorID = *value
|
||||
}
|
||||
case platform.FieldConfigForm:
|
||||
case platform.FieldForm:
|
||||
if value, ok := values[i].(*[]byte); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field config_form", values[i])
|
||||
} else if value != nil {
|
||||
pl.ConfigForm = *value
|
||||
return fmt.Errorf("unexpected type %T for field form", values[i])
|
||||
} else if value != nil && len(*value) > 0 {
|
||||
if err := json.Unmarshal(*value, &pl.Form); err != nil {
|
||||
return fmt.Errorf("unmarshal field form: %w", err)
|
||||
}
|
||||
}
|
||||
case platform.FieldConfigValues:
|
||||
case platform.FieldModel:
|
||||
if value, ok := values[i].(*[]byte); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field config_values", values[i])
|
||||
} else if value != nil {
|
||||
pl.ConfigValues = *value
|
||||
return fmt.Errorf("unexpected type %T for field model", values[i])
|
||||
} else if value != nil && len(*value) > 0 {
|
||||
if err := json.Unmarshal(*value, &pl.Model); err != nil {
|
||||
return fmt.Errorf("unmarshal field model: %w", err)
|
||||
}
|
||||
}
|
||||
case platform.FieldConfigCue:
|
||||
case platform.FieldCue:
|
||||
if value, ok := values[i].(*[]byte); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field config_cue", values[i])
|
||||
return fmt.Errorf("unexpected type %T for field cue", values[i])
|
||||
} else if value != nil {
|
||||
pl.ConfigCue = *value
|
||||
pl.Cue = *value
|
||||
}
|
||||
case platform.FieldConfigDefinition:
|
||||
case platform.FieldCueDefinition:
|
||||
if value, ok := values[i].(*sql.NullString); !ok {
|
||||
return fmt.Errorf("unexpected type %T for field config_definition", values[i])
|
||||
return fmt.Errorf("unexpected type %T for field cue_definition", values[i])
|
||||
} else if value.Valid {
|
||||
pl.ConfigDefinition = value.String
|
||||
pl.CueDefinition = value.String
|
||||
}
|
||||
default:
|
||||
pl.selectValues.Set(columns[i], values[i])
|
||||
@@ -237,17 +243,17 @@ func (pl *Platform) String() string {
|
||||
builder.WriteString("creator_id=")
|
||||
builder.WriteString(fmt.Sprintf("%v", pl.CreatorID))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("config_form=")
|
||||
builder.WriteString(fmt.Sprintf("%v", pl.ConfigForm))
|
||||
builder.WriteString("form=")
|
||||
builder.WriteString(fmt.Sprintf("%v", pl.Form))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("config_values=")
|
||||
builder.WriteString(fmt.Sprintf("%v", pl.ConfigValues))
|
||||
builder.WriteString("model=")
|
||||
builder.WriteString(fmt.Sprintf("%v", pl.Model))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("config_cue=")
|
||||
builder.WriteString(fmt.Sprintf("%v", pl.ConfigCue))
|
||||
builder.WriteString("cue=")
|
||||
builder.WriteString(fmt.Sprintf("%v", pl.Cue))
|
||||
builder.WriteString(", ")
|
||||
builder.WriteString("config_definition=")
|
||||
builder.WriteString(pl.ConfigDefinition)
|
||||
builder.WriteString("cue_definition=")
|
||||
builder.WriteString(pl.CueDefinition)
|
||||
builder.WriteByte(')')
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
@@ -27,14 +27,14 @@ const (
|
||||
FieldDisplayName = "display_name"
|
||||
// FieldCreatorID holds the string denoting the creator_id field in the database.
|
||||
FieldCreatorID = "creator_id"
|
||||
// FieldConfigForm holds the string denoting the config_form field in the database.
|
||||
FieldConfigForm = "config_form"
|
||||
// FieldConfigValues holds the string denoting the config_values field in the database.
|
||||
FieldConfigValues = "config_values"
|
||||
// FieldConfigCue holds the string denoting the config_cue field in the database.
|
||||
FieldConfigCue = "config_cue"
|
||||
// FieldConfigDefinition holds the string denoting the config_definition field in the database.
|
||||
FieldConfigDefinition = "config_definition"
|
||||
// FieldForm holds the string denoting the form field in the database.
|
||||
FieldForm = "form"
|
||||
// FieldModel holds the string denoting the model field in the database.
|
||||
FieldModel = "model"
|
||||
// FieldCue holds the string denoting the cue field in the database.
|
||||
FieldCue = "cue"
|
||||
// FieldCueDefinition holds the string denoting the cue_definition field in the database.
|
||||
FieldCueDefinition = "cue_definition"
|
||||
// EdgeCreator holds the string denoting the creator edge name in mutations.
|
||||
EdgeCreator = "creator"
|
||||
// EdgeOrganization holds the string denoting the organization edge name in mutations.
|
||||
@@ -66,10 +66,10 @@ var Columns = []string{
|
||||
FieldName,
|
||||
FieldDisplayName,
|
||||
FieldCreatorID,
|
||||
FieldConfigForm,
|
||||
FieldConfigValues,
|
||||
FieldConfigCue,
|
||||
FieldConfigDefinition,
|
||||
FieldForm,
|
||||
FieldModel,
|
||||
FieldCue,
|
||||
FieldCueDefinition,
|
||||
}
|
||||
|
||||
// ValidColumn reports if the column name is valid (part of the table columns).
|
||||
@@ -133,9 +133,9 @@ func ByCreatorID(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCreatorID, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByConfigDefinition orders the results by the config_definition field.
|
||||
func ByConfigDefinition(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldConfigDefinition, opts...).ToFunc()
|
||||
// ByCueDefinition orders the results by the cue_definition field.
|
||||
func ByCueDefinition(opts ...sql.OrderTermOption) OrderOption {
|
||||
return sql.OrderByField(FieldCueDefinition, opts...).ToFunc()
|
||||
}
|
||||
|
||||
// ByCreatorField orders the results by creator field.
|
||||
|
||||
@@ -86,24 +86,14 @@ func CreatorID(v uuid.UUID) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldCreatorID, v))
|
||||
}
|
||||
|
||||
// ConfigForm applies equality check predicate on the "config_form" field. It's identical to ConfigFormEQ.
|
||||
func ConfigForm(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigForm, v))
|
||||
// Cue applies equality check predicate on the "cue" field. It's identical to CueEQ.
|
||||
func Cue(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldCue, v))
|
||||
}
|
||||
|
||||
// ConfigValues applies equality check predicate on the "config_values" field. It's identical to ConfigValuesEQ.
|
||||
func ConfigValues(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigValues, v))
|
||||
}
|
||||
|
||||
// ConfigCue applies equality check predicate on the "config_cue" field. It's identical to ConfigCueEQ.
|
||||
func ConfigCue(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigCue, v))
|
||||
}
|
||||
|
||||
// ConfigDefinition applies equality check predicate on the "config_definition" field. It's identical to ConfigDefinitionEQ.
|
||||
func ConfigDefinition(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigDefinition, v))
|
||||
// CueDefinition applies equality check predicate on the "cue_definition" field. It's identical to CueDefinitionEQ.
|
||||
func CueDefinition(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
|
||||
@@ -356,229 +346,149 @@ func CreatorIDNotIn(vs ...uuid.UUID) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldCreatorID, vs...))
|
||||
}
|
||||
|
||||
// ConfigFormEQ applies the EQ predicate on the "config_form" field.
|
||||
func ConfigFormEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigForm, v))
|
||||
// FormIsNil applies the IsNil predicate on the "form" field.
|
||||
func FormIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldForm))
|
||||
}
|
||||
|
||||
// ConfigFormNEQ applies the NEQ predicate on the "config_form" field.
|
||||
func ConfigFormNEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldConfigForm, v))
|
||||
// FormNotNil applies the NotNil predicate on the "form" field.
|
||||
func FormNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldForm))
|
||||
}
|
||||
|
||||
// ConfigFormIn applies the In predicate on the "config_form" field.
|
||||
func ConfigFormIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldConfigForm, vs...))
|
||||
// ModelIsNil applies the IsNil predicate on the "model" field.
|
||||
func ModelIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldModel))
|
||||
}
|
||||
|
||||
// ConfigFormNotIn applies the NotIn predicate on the "config_form" field.
|
||||
func ConfigFormNotIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldConfigForm, vs...))
|
||||
// ModelNotNil applies the NotNil predicate on the "model" field.
|
||||
func ModelNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldModel))
|
||||
}
|
||||
|
||||
// ConfigFormGT applies the GT predicate on the "config_form" field.
|
||||
func ConfigFormGT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldConfigForm, v))
|
||||
// CueEQ applies the EQ predicate on the "cue" field.
|
||||
func CueEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldCue, v))
|
||||
}
|
||||
|
||||
// ConfigFormGTE applies the GTE predicate on the "config_form" field.
|
||||
func ConfigFormGTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldConfigForm, v))
|
||||
// CueNEQ applies the NEQ predicate on the "cue" field.
|
||||
func CueNEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldCue, v))
|
||||
}
|
||||
|
||||
// ConfigFormLT applies the LT predicate on the "config_form" field.
|
||||
func ConfigFormLT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldConfigForm, v))
|
||||
// CueIn applies the In predicate on the "cue" field.
|
||||
func CueIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldCue, vs...))
|
||||
}
|
||||
|
||||
// ConfigFormLTE applies the LTE predicate on the "config_form" field.
|
||||
func ConfigFormLTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldConfigForm, v))
|
||||
// CueNotIn applies the NotIn predicate on the "cue" field.
|
||||
func CueNotIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldCue, vs...))
|
||||
}
|
||||
|
||||
// ConfigFormIsNil applies the IsNil predicate on the "config_form" field.
|
||||
func ConfigFormIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldConfigForm))
|
||||
// CueGT applies the GT predicate on the "cue" field.
|
||||
func CueGT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldCue, v))
|
||||
}
|
||||
|
||||
// ConfigFormNotNil applies the NotNil predicate on the "config_form" field.
|
||||
func ConfigFormNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldConfigForm))
|
||||
// CueGTE applies the GTE predicate on the "cue" field.
|
||||
func CueGTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldCue, v))
|
||||
}
|
||||
|
||||
// ConfigValuesEQ applies the EQ predicate on the "config_values" field.
|
||||
func ConfigValuesEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigValues, v))
|
||||
// CueLT applies the LT predicate on the "cue" field.
|
||||
func CueLT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldCue, v))
|
||||
}
|
||||
|
||||
// ConfigValuesNEQ applies the NEQ predicate on the "config_values" field.
|
||||
func ConfigValuesNEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldConfigValues, v))
|
||||
// CueLTE applies the LTE predicate on the "cue" field.
|
||||
func CueLTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldCue, v))
|
||||
}
|
||||
|
||||
// ConfigValuesIn applies the In predicate on the "config_values" field.
|
||||
func ConfigValuesIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldConfigValues, vs...))
|
||||
// CueIsNil applies the IsNil predicate on the "cue" field.
|
||||
func CueIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldCue))
|
||||
}
|
||||
|
||||
// ConfigValuesNotIn applies the NotIn predicate on the "config_values" field.
|
||||
func ConfigValuesNotIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldConfigValues, vs...))
|
||||
// CueNotNil applies the NotNil predicate on the "cue" field.
|
||||
func CueNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldCue))
|
||||
}
|
||||
|
||||
// ConfigValuesGT applies the GT predicate on the "config_values" field.
|
||||
func ConfigValuesGT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldConfigValues, v))
|
||||
// CueDefinitionEQ applies the EQ predicate on the "cue_definition" field.
|
||||
func CueDefinitionEQ(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigValuesGTE applies the GTE predicate on the "config_values" field.
|
||||
func ConfigValuesGTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldConfigValues, v))
|
||||
// CueDefinitionNEQ applies the NEQ predicate on the "cue_definition" field.
|
||||
func CueDefinitionNEQ(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigValuesLT applies the LT predicate on the "config_values" field.
|
||||
func ConfigValuesLT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldConfigValues, v))
|
||||
// CueDefinitionIn applies the In predicate on the "cue_definition" field.
|
||||
func CueDefinitionIn(vs ...string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldCueDefinition, vs...))
|
||||
}
|
||||
|
||||
// ConfigValuesLTE applies the LTE predicate on the "config_values" field.
|
||||
func ConfigValuesLTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldConfigValues, v))
|
||||
// CueDefinitionNotIn applies the NotIn predicate on the "cue_definition" field.
|
||||
func CueDefinitionNotIn(vs ...string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldCueDefinition, vs...))
|
||||
}
|
||||
|
||||
// ConfigValuesIsNil applies the IsNil predicate on the "config_values" field.
|
||||
func ConfigValuesIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldConfigValues))
|
||||
// CueDefinitionGT applies the GT predicate on the "cue_definition" field.
|
||||
func CueDefinitionGT(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigValuesNotNil applies the NotNil predicate on the "config_values" field.
|
||||
func ConfigValuesNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldConfigValues))
|
||||
// CueDefinitionGTE applies the GTE predicate on the "cue_definition" field.
|
||||
func CueDefinitionGTE(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigCueEQ applies the EQ predicate on the "config_cue" field.
|
||||
func ConfigCueEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigCue, v))
|
||||
// CueDefinitionLT applies the LT predicate on the "cue_definition" field.
|
||||
func CueDefinitionLT(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigCueNEQ applies the NEQ predicate on the "config_cue" field.
|
||||
func ConfigCueNEQ(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldConfigCue, v))
|
||||
// CueDefinitionLTE applies the LTE predicate on the "cue_definition" field.
|
||||
func CueDefinitionLTE(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigCueIn applies the In predicate on the "config_cue" field.
|
||||
func ConfigCueIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldConfigCue, vs...))
|
||||
// CueDefinitionContains applies the Contains predicate on the "cue_definition" field.
|
||||
func CueDefinitionContains(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldContains(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigCueNotIn applies the NotIn predicate on the "config_cue" field.
|
||||
func ConfigCueNotIn(vs ...[]byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldConfigCue, vs...))
|
||||
// CueDefinitionHasPrefix applies the HasPrefix predicate on the "cue_definition" field.
|
||||
func CueDefinitionHasPrefix(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldHasPrefix(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigCueGT applies the GT predicate on the "config_cue" field.
|
||||
func ConfigCueGT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldConfigCue, v))
|
||||
// CueDefinitionHasSuffix applies the HasSuffix predicate on the "cue_definition" field.
|
||||
func CueDefinitionHasSuffix(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldHasSuffix(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigCueGTE applies the GTE predicate on the "config_cue" field.
|
||||
func ConfigCueGTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldConfigCue, v))
|
||||
// CueDefinitionIsNil applies the IsNil predicate on the "cue_definition" field.
|
||||
func CueDefinitionIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldCueDefinition))
|
||||
}
|
||||
|
||||
// ConfigCueLT applies the LT predicate on the "config_cue" field.
|
||||
func ConfigCueLT(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldConfigCue, v))
|
||||
// CueDefinitionNotNil applies the NotNil predicate on the "cue_definition" field.
|
||||
func CueDefinitionNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldCueDefinition))
|
||||
}
|
||||
|
||||
// ConfigCueLTE applies the LTE predicate on the "config_cue" field.
|
||||
func ConfigCueLTE(v []byte) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldConfigCue, v))
|
||||
// CueDefinitionEqualFold applies the EqualFold predicate on the "cue_definition" field.
|
||||
func CueDefinitionEqualFold(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEqualFold(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigCueIsNil applies the IsNil predicate on the "config_cue" field.
|
||||
func ConfigCueIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldConfigCue))
|
||||
}
|
||||
|
||||
// ConfigCueNotNil applies the NotNil predicate on the "config_cue" field.
|
||||
func ConfigCueNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldConfigCue))
|
||||
}
|
||||
|
||||
// ConfigDefinitionEQ applies the EQ predicate on the "config_definition" field.
|
||||
func ConfigDefinitionEQ(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEQ(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionNEQ applies the NEQ predicate on the "config_definition" field.
|
||||
func ConfigDefinitionNEQ(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNEQ(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionIn applies the In predicate on the "config_definition" field.
|
||||
func ConfigDefinitionIn(vs ...string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIn(FieldConfigDefinition, vs...))
|
||||
}
|
||||
|
||||
// ConfigDefinitionNotIn applies the NotIn predicate on the "config_definition" field.
|
||||
func ConfigDefinitionNotIn(vs ...string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotIn(FieldConfigDefinition, vs...))
|
||||
}
|
||||
|
||||
// ConfigDefinitionGT applies the GT predicate on the "config_definition" field.
|
||||
func ConfigDefinitionGT(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGT(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionGTE applies the GTE predicate on the "config_definition" field.
|
||||
func ConfigDefinitionGTE(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldGTE(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionLT applies the LT predicate on the "config_definition" field.
|
||||
func ConfigDefinitionLT(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLT(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionLTE applies the LTE predicate on the "config_definition" field.
|
||||
func ConfigDefinitionLTE(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldLTE(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionContains applies the Contains predicate on the "config_definition" field.
|
||||
func ConfigDefinitionContains(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldContains(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionHasPrefix applies the HasPrefix predicate on the "config_definition" field.
|
||||
func ConfigDefinitionHasPrefix(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldHasPrefix(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionHasSuffix applies the HasSuffix predicate on the "config_definition" field.
|
||||
func ConfigDefinitionHasSuffix(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldHasSuffix(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionIsNil applies the IsNil predicate on the "config_definition" field.
|
||||
func ConfigDefinitionIsNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldIsNull(FieldConfigDefinition))
|
||||
}
|
||||
|
||||
// ConfigDefinitionNotNil applies the NotNil predicate on the "config_definition" field.
|
||||
func ConfigDefinitionNotNil() predicate.Platform {
|
||||
return predicate.Platform(sql.FieldNotNull(FieldConfigDefinition))
|
||||
}
|
||||
|
||||
// ConfigDefinitionEqualFold applies the EqualFold predicate on the "config_definition" field.
|
||||
func ConfigDefinitionEqualFold(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldEqualFold(FieldConfigDefinition, v))
|
||||
}
|
||||
|
||||
// ConfigDefinitionContainsFold applies the ContainsFold predicate on the "config_definition" field.
|
||||
func ConfigDefinitionContainsFold(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldContainsFold(FieldConfigDefinition, v))
|
||||
// CueDefinitionContainsFold applies the ContainsFold predicate on the "cue_definition" field.
|
||||
func CueDefinitionContainsFold(v string) predicate.Platform {
|
||||
return predicate.Platform(sql.FieldContainsFold(FieldCueDefinition, v))
|
||||
}
|
||||
|
||||
// HasCreator applies the HasEdge predicate on the "creator" edge.
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
// PlatformCreate is the builder for creating a Platform entity.
|
||||
@@ -78,34 +79,34 @@ func (pc *PlatformCreate) SetCreatorID(u uuid.UUID) *PlatformCreate {
|
||||
return pc
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (pc *PlatformCreate) SetConfigForm(b []byte) *PlatformCreate {
|
||||
pc.mutation.SetConfigForm(b)
|
||||
// SetForm sets the "form" field.
|
||||
func (pc *PlatformCreate) SetForm(h *holos.Form) *PlatformCreate {
|
||||
pc.mutation.SetForm(h)
|
||||
return pc
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (pc *PlatformCreate) SetConfigValues(b []byte) *PlatformCreate {
|
||||
pc.mutation.SetConfigValues(b)
|
||||
// SetModel sets the "model" field.
|
||||
func (pc *PlatformCreate) SetModel(h *holos.Model) *PlatformCreate {
|
||||
pc.mutation.SetModel(h)
|
||||
return pc
|
||||
}
|
||||
|
||||
// SetConfigCue sets the "config_cue" field.
|
||||
func (pc *PlatformCreate) SetConfigCue(b []byte) *PlatformCreate {
|
||||
pc.mutation.SetConfigCue(b)
|
||||
// SetCue sets the "cue" field.
|
||||
func (pc *PlatformCreate) SetCue(b []byte) *PlatformCreate {
|
||||
pc.mutation.SetCue(b)
|
||||
return pc
|
||||
}
|
||||
|
||||
// SetConfigDefinition sets the "config_definition" field.
|
||||
func (pc *PlatformCreate) SetConfigDefinition(s string) *PlatformCreate {
|
||||
pc.mutation.SetConfigDefinition(s)
|
||||
// SetCueDefinition sets the "cue_definition" field.
|
||||
func (pc *PlatformCreate) SetCueDefinition(s string) *PlatformCreate {
|
||||
pc.mutation.SetCueDefinition(s)
|
||||
return pc
|
||||
}
|
||||
|
||||
// SetNillableConfigDefinition sets the "config_definition" field if the given value is not nil.
|
||||
func (pc *PlatformCreate) SetNillableConfigDefinition(s *string) *PlatformCreate {
|
||||
// SetNillableCueDefinition sets the "cue_definition" field if the given value is not nil.
|
||||
func (pc *PlatformCreate) SetNillableCueDefinition(s *string) *PlatformCreate {
|
||||
if s != nil {
|
||||
pc.SetConfigDefinition(*s)
|
||||
pc.SetCueDefinition(*s)
|
||||
}
|
||||
return pc
|
||||
}
|
||||
@@ -272,21 +273,21 @@ func (pc *PlatformCreate) createSpec() (*Platform, *sqlgraph.CreateSpec) {
|
||||
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
|
||||
_node.DisplayName = value
|
||||
}
|
||||
if value, ok := pc.mutation.ConfigForm(); ok {
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
|
||||
_node.ConfigForm = value
|
||||
if value, ok := pc.mutation.Form(); ok {
|
||||
_spec.SetField(platform.FieldForm, field.TypeJSON, value)
|
||||
_node.Form = value
|
||||
}
|
||||
if value, ok := pc.mutation.ConfigValues(); ok {
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
|
||||
_node.ConfigValues = value
|
||||
if value, ok := pc.mutation.Model(); ok {
|
||||
_spec.SetField(platform.FieldModel, field.TypeJSON, value)
|
||||
_node.Model = value
|
||||
}
|
||||
if value, ok := pc.mutation.ConfigCue(); ok {
|
||||
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
|
||||
_node.ConfigCue = value
|
||||
if value, ok := pc.mutation.Cue(); ok {
|
||||
_spec.SetField(platform.FieldCue, field.TypeBytes, value)
|
||||
_node.Cue = value
|
||||
}
|
||||
if value, ok := pc.mutation.ConfigDefinition(); ok {
|
||||
_spec.SetField(platform.FieldConfigDefinition, field.TypeString, value)
|
||||
_node.ConfigDefinition = value
|
||||
if value, ok := pc.mutation.CueDefinition(); ok {
|
||||
_spec.SetField(platform.FieldCueDefinition, field.TypeString, value)
|
||||
_node.CueDefinition = value
|
||||
}
|
||||
if nodes := pc.mutation.CreatorIDs(); len(nodes) > 0 {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
@@ -434,75 +435,75 @@ func (u *PlatformUpsert) UpdateCreatorID() *PlatformUpsert {
|
||||
return u
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (u *PlatformUpsert) SetConfigForm(v []byte) *PlatformUpsert {
|
||||
u.Set(platform.FieldConfigForm, v)
|
||||
// SetForm sets the "form" field.
|
||||
func (u *PlatformUpsert) SetForm(v *holos.Form) *PlatformUpsert {
|
||||
u.Set(platform.FieldForm, v)
|
||||
return u
|
||||
}
|
||||
|
||||
// UpdateConfigForm sets the "config_form" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateConfigForm() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldConfigForm)
|
||||
// UpdateForm sets the "form" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateForm() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldForm)
|
||||
return u
|
||||
}
|
||||
|
||||
// ClearConfigForm clears the value of the "config_form" field.
|
||||
func (u *PlatformUpsert) ClearConfigForm() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldConfigForm)
|
||||
// ClearForm clears the value of the "form" field.
|
||||
func (u *PlatformUpsert) ClearForm() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldForm)
|
||||
return u
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (u *PlatformUpsert) SetConfigValues(v []byte) *PlatformUpsert {
|
||||
u.Set(platform.FieldConfigValues, v)
|
||||
// SetModel sets the "model" field.
|
||||
func (u *PlatformUpsert) SetModel(v *holos.Model) *PlatformUpsert {
|
||||
u.Set(platform.FieldModel, v)
|
||||
return u
|
||||
}
|
||||
|
||||
// UpdateConfigValues sets the "config_values" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateConfigValues() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldConfigValues)
|
||||
// UpdateModel sets the "model" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateModel() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldModel)
|
||||
return u
|
||||
}
|
||||
|
||||
// ClearConfigValues clears the value of the "config_values" field.
|
||||
func (u *PlatformUpsert) ClearConfigValues() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldConfigValues)
|
||||
// ClearModel clears the value of the "model" field.
|
||||
func (u *PlatformUpsert) ClearModel() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldModel)
|
||||
return u
|
||||
}
|
||||
|
||||
// SetConfigCue sets the "config_cue" field.
|
||||
func (u *PlatformUpsert) SetConfigCue(v []byte) *PlatformUpsert {
|
||||
u.Set(platform.FieldConfigCue, v)
|
||||
// SetCue sets the "cue" field.
|
||||
func (u *PlatformUpsert) SetCue(v []byte) *PlatformUpsert {
|
||||
u.Set(platform.FieldCue, v)
|
||||
return u
|
||||
}
|
||||
|
||||
// UpdateConfigCue sets the "config_cue" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateConfigCue() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldConfigCue)
|
||||
// UpdateCue sets the "cue" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateCue() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldCue)
|
||||
return u
|
||||
}
|
||||
|
||||
// ClearConfigCue clears the value of the "config_cue" field.
|
||||
func (u *PlatformUpsert) ClearConfigCue() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldConfigCue)
|
||||
// ClearCue clears the value of the "cue" field.
|
||||
func (u *PlatformUpsert) ClearCue() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldCue)
|
||||
return u
|
||||
}
|
||||
|
||||
// SetConfigDefinition sets the "config_definition" field.
|
||||
func (u *PlatformUpsert) SetConfigDefinition(v string) *PlatformUpsert {
|
||||
u.Set(platform.FieldConfigDefinition, v)
|
||||
// SetCueDefinition sets the "cue_definition" field.
|
||||
func (u *PlatformUpsert) SetCueDefinition(v string) *PlatformUpsert {
|
||||
u.Set(platform.FieldCueDefinition, v)
|
||||
return u
|
||||
}
|
||||
|
||||
// UpdateConfigDefinition sets the "config_definition" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateConfigDefinition() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldConfigDefinition)
|
||||
// UpdateCueDefinition sets the "cue_definition" field to the value that was provided on create.
|
||||
func (u *PlatformUpsert) UpdateCueDefinition() *PlatformUpsert {
|
||||
u.SetExcluded(platform.FieldCueDefinition)
|
||||
return u
|
||||
}
|
||||
|
||||
// ClearConfigDefinition clears the value of the "config_definition" field.
|
||||
func (u *PlatformUpsert) ClearConfigDefinition() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldConfigDefinition)
|
||||
// ClearCueDefinition clears the value of the "cue_definition" field.
|
||||
func (u *PlatformUpsert) ClearCueDefinition() *PlatformUpsert {
|
||||
u.SetNull(platform.FieldCueDefinition)
|
||||
return u
|
||||
}
|
||||
|
||||
@@ -627,87 +628,87 @@ func (u *PlatformUpsertOne) UpdateCreatorID() *PlatformUpsertOne {
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (u *PlatformUpsertOne) SetConfigForm(v []byte) *PlatformUpsertOne {
|
||||
// SetForm sets the "form" field.
|
||||
func (u *PlatformUpsertOne) SetForm(v *holos.Form) *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigForm(v)
|
||||
s.SetForm(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigForm sets the "config_form" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateConfigForm() *PlatformUpsertOne {
|
||||
// UpdateForm sets the "form" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateForm() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigForm()
|
||||
s.UpdateForm()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigForm clears the value of the "config_form" field.
|
||||
func (u *PlatformUpsertOne) ClearConfigForm() *PlatformUpsertOne {
|
||||
// ClearForm clears the value of the "form" field.
|
||||
func (u *PlatformUpsertOne) ClearForm() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigForm()
|
||||
s.ClearForm()
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (u *PlatformUpsertOne) SetConfigValues(v []byte) *PlatformUpsertOne {
|
||||
// SetModel sets the "model" field.
|
||||
func (u *PlatformUpsertOne) SetModel(v *holos.Model) *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigValues(v)
|
||||
s.SetModel(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigValues sets the "config_values" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateConfigValues() *PlatformUpsertOne {
|
||||
// UpdateModel sets the "model" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateModel() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigValues()
|
||||
s.UpdateModel()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigValues clears the value of the "config_values" field.
|
||||
func (u *PlatformUpsertOne) ClearConfigValues() *PlatformUpsertOne {
|
||||
// ClearModel clears the value of the "model" field.
|
||||
func (u *PlatformUpsertOne) ClearModel() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigValues()
|
||||
s.ClearModel()
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigCue sets the "config_cue" field.
|
||||
func (u *PlatformUpsertOne) SetConfigCue(v []byte) *PlatformUpsertOne {
|
||||
// SetCue sets the "cue" field.
|
||||
func (u *PlatformUpsertOne) SetCue(v []byte) *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigCue(v)
|
||||
s.SetCue(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigCue sets the "config_cue" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateConfigCue() *PlatformUpsertOne {
|
||||
// UpdateCue sets the "cue" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateCue() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigCue()
|
||||
s.UpdateCue()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigCue clears the value of the "config_cue" field.
|
||||
func (u *PlatformUpsertOne) ClearConfigCue() *PlatformUpsertOne {
|
||||
// ClearCue clears the value of the "cue" field.
|
||||
func (u *PlatformUpsertOne) ClearCue() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigCue()
|
||||
s.ClearCue()
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigDefinition sets the "config_definition" field.
|
||||
func (u *PlatformUpsertOne) SetConfigDefinition(v string) *PlatformUpsertOne {
|
||||
// SetCueDefinition sets the "cue_definition" field.
|
||||
func (u *PlatformUpsertOne) SetCueDefinition(v string) *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigDefinition(v)
|
||||
s.SetCueDefinition(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigDefinition sets the "config_definition" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateConfigDefinition() *PlatformUpsertOne {
|
||||
// UpdateCueDefinition sets the "cue_definition" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertOne) UpdateCueDefinition() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigDefinition()
|
||||
s.UpdateCueDefinition()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigDefinition clears the value of the "config_definition" field.
|
||||
func (u *PlatformUpsertOne) ClearConfigDefinition() *PlatformUpsertOne {
|
||||
// ClearCueDefinition clears the value of the "cue_definition" field.
|
||||
func (u *PlatformUpsertOne) ClearCueDefinition() *PlatformUpsertOne {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigDefinition()
|
||||
s.ClearCueDefinition()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -999,87 +1000,87 @@ func (u *PlatformUpsertBulk) UpdateCreatorID() *PlatformUpsertBulk {
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (u *PlatformUpsertBulk) SetConfigForm(v []byte) *PlatformUpsertBulk {
|
||||
// SetForm sets the "form" field.
|
||||
func (u *PlatformUpsertBulk) SetForm(v *holos.Form) *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigForm(v)
|
||||
s.SetForm(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigForm sets the "config_form" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateConfigForm() *PlatformUpsertBulk {
|
||||
// UpdateForm sets the "form" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateForm() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigForm()
|
||||
s.UpdateForm()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigForm clears the value of the "config_form" field.
|
||||
func (u *PlatformUpsertBulk) ClearConfigForm() *PlatformUpsertBulk {
|
||||
// ClearForm clears the value of the "form" field.
|
||||
func (u *PlatformUpsertBulk) ClearForm() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigForm()
|
||||
s.ClearForm()
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (u *PlatformUpsertBulk) SetConfigValues(v []byte) *PlatformUpsertBulk {
|
||||
// SetModel sets the "model" field.
|
||||
func (u *PlatformUpsertBulk) SetModel(v *holos.Model) *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigValues(v)
|
||||
s.SetModel(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigValues sets the "config_values" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateConfigValues() *PlatformUpsertBulk {
|
||||
// UpdateModel sets the "model" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateModel() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigValues()
|
||||
s.UpdateModel()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigValues clears the value of the "config_values" field.
|
||||
func (u *PlatformUpsertBulk) ClearConfigValues() *PlatformUpsertBulk {
|
||||
// ClearModel clears the value of the "model" field.
|
||||
func (u *PlatformUpsertBulk) ClearModel() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigValues()
|
||||
s.ClearModel()
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigCue sets the "config_cue" field.
|
||||
func (u *PlatformUpsertBulk) SetConfigCue(v []byte) *PlatformUpsertBulk {
|
||||
// SetCue sets the "cue" field.
|
||||
func (u *PlatformUpsertBulk) SetCue(v []byte) *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigCue(v)
|
||||
s.SetCue(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigCue sets the "config_cue" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateConfigCue() *PlatformUpsertBulk {
|
||||
// UpdateCue sets the "cue" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateCue() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigCue()
|
||||
s.UpdateCue()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigCue clears the value of the "config_cue" field.
|
||||
func (u *PlatformUpsertBulk) ClearConfigCue() *PlatformUpsertBulk {
|
||||
// ClearCue clears the value of the "cue" field.
|
||||
func (u *PlatformUpsertBulk) ClearCue() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigCue()
|
||||
s.ClearCue()
|
||||
})
|
||||
}
|
||||
|
||||
// SetConfigDefinition sets the "config_definition" field.
|
||||
func (u *PlatformUpsertBulk) SetConfigDefinition(v string) *PlatformUpsertBulk {
|
||||
// SetCueDefinition sets the "cue_definition" field.
|
||||
func (u *PlatformUpsertBulk) SetCueDefinition(v string) *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.SetConfigDefinition(v)
|
||||
s.SetCueDefinition(v)
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateConfigDefinition sets the "config_definition" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateConfigDefinition() *PlatformUpsertBulk {
|
||||
// UpdateCueDefinition sets the "cue_definition" field to the value that was provided on create.
|
||||
func (u *PlatformUpsertBulk) UpdateCueDefinition() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.UpdateConfigDefinition()
|
||||
s.UpdateCueDefinition()
|
||||
})
|
||||
}
|
||||
|
||||
// ClearConfigDefinition clears the value of the "config_definition" field.
|
||||
func (u *PlatformUpsertBulk) ClearConfigDefinition() *PlatformUpsertBulk {
|
||||
// ClearCueDefinition clears the value of the "cue_definition" field.
|
||||
func (u *PlatformUpsertBulk) ClearCueDefinition() *PlatformUpsertBulk {
|
||||
return u.Update(func(s *PlatformUpsert) {
|
||||
s.ClearConfigDefinition()
|
||||
s.ClearCueDefinition()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/predicate"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
// PlatformUpdate is the builder for updating Platform entities.
|
||||
@@ -93,59 +94,59 @@ func (pu *PlatformUpdate) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate {
|
||||
return pu
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (pu *PlatformUpdate) SetConfigForm(b []byte) *PlatformUpdate {
|
||||
pu.mutation.SetConfigForm(b)
|
||||
// SetForm sets the "form" field.
|
||||
func (pu *PlatformUpdate) SetForm(h *holos.Form) *PlatformUpdate {
|
||||
pu.mutation.SetForm(h)
|
||||
return pu
|
||||
}
|
||||
|
||||
// ClearConfigForm clears the value of the "config_form" field.
|
||||
func (pu *PlatformUpdate) ClearConfigForm() *PlatformUpdate {
|
||||
pu.mutation.ClearConfigForm()
|
||||
// ClearForm clears the value of the "form" field.
|
||||
func (pu *PlatformUpdate) ClearForm() *PlatformUpdate {
|
||||
pu.mutation.ClearForm()
|
||||
return pu
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (pu *PlatformUpdate) SetConfigValues(b []byte) *PlatformUpdate {
|
||||
pu.mutation.SetConfigValues(b)
|
||||
// SetModel sets the "model" field.
|
||||
func (pu *PlatformUpdate) SetModel(h *holos.Model) *PlatformUpdate {
|
||||
pu.mutation.SetModel(h)
|
||||
return pu
|
||||
}
|
||||
|
||||
// ClearConfigValues clears the value of the "config_values" field.
|
||||
func (pu *PlatformUpdate) ClearConfigValues() *PlatformUpdate {
|
||||
pu.mutation.ClearConfigValues()
|
||||
// ClearModel clears the value of the "model" field.
|
||||
func (pu *PlatformUpdate) ClearModel() *PlatformUpdate {
|
||||
pu.mutation.ClearModel()
|
||||
return pu
|
||||
}
|
||||
|
||||
// SetConfigCue sets the "config_cue" field.
|
||||
func (pu *PlatformUpdate) SetConfigCue(b []byte) *PlatformUpdate {
|
||||
pu.mutation.SetConfigCue(b)
|
||||
// SetCue sets the "cue" field.
|
||||
func (pu *PlatformUpdate) SetCue(b []byte) *PlatformUpdate {
|
||||
pu.mutation.SetCue(b)
|
||||
return pu
|
||||
}
|
||||
|
||||
// ClearConfigCue clears the value of the "config_cue" field.
|
||||
func (pu *PlatformUpdate) ClearConfigCue() *PlatformUpdate {
|
||||
pu.mutation.ClearConfigCue()
|
||||
// ClearCue clears the value of the "cue" field.
|
||||
func (pu *PlatformUpdate) ClearCue() *PlatformUpdate {
|
||||
pu.mutation.ClearCue()
|
||||
return pu
|
||||
}
|
||||
|
||||
// SetConfigDefinition sets the "config_definition" field.
|
||||
func (pu *PlatformUpdate) SetConfigDefinition(s string) *PlatformUpdate {
|
||||
pu.mutation.SetConfigDefinition(s)
|
||||
// SetCueDefinition sets the "cue_definition" field.
|
||||
func (pu *PlatformUpdate) SetCueDefinition(s string) *PlatformUpdate {
|
||||
pu.mutation.SetCueDefinition(s)
|
||||
return pu
|
||||
}
|
||||
|
||||
// SetNillableConfigDefinition sets the "config_definition" field if the given value is not nil.
|
||||
func (pu *PlatformUpdate) SetNillableConfigDefinition(s *string) *PlatformUpdate {
|
||||
// SetNillableCueDefinition sets the "cue_definition" field if the given value is not nil.
|
||||
func (pu *PlatformUpdate) SetNillableCueDefinition(s *string) *PlatformUpdate {
|
||||
if s != nil {
|
||||
pu.SetConfigDefinition(*s)
|
||||
pu.SetCueDefinition(*s)
|
||||
}
|
||||
return pu
|
||||
}
|
||||
|
||||
// ClearConfigDefinition clears the value of the "config_definition" field.
|
||||
func (pu *PlatformUpdate) ClearConfigDefinition() *PlatformUpdate {
|
||||
pu.mutation.ClearConfigDefinition()
|
||||
// ClearCueDefinition clears the value of the "cue_definition" field.
|
||||
func (pu *PlatformUpdate) ClearCueDefinition() *PlatformUpdate {
|
||||
pu.mutation.ClearCueDefinition()
|
||||
return pu
|
||||
}
|
||||
|
||||
@@ -255,29 +256,29 @@ func (pu *PlatformUpdate) sqlSave(ctx context.Context) (n int, err error) {
|
||||
if value, ok := pu.mutation.DisplayName(); ok {
|
||||
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := pu.mutation.ConfigForm(); ok {
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
|
||||
if value, ok := pu.mutation.Form(); ok {
|
||||
_spec.SetField(platform.FieldForm, field.TypeJSON, value)
|
||||
}
|
||||
if pu.mutation.ConfigFormCleared() {
|
||||
_spec.ClearField(platform.FieldConfigForm, field.TypeBytes)
|
||||
if pu.mutation.FormCleared() {
|
||||
_spec.ClearField(platform.FieldForm, field.TypeJSON)
|
||||
}
|
||||
if value, ok := pu.mutation.ConfigValues(); ok {
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
|
||||
if value, ok := pu.mutation.Model(); ok {
|
||||
_spec.SetField(platform.FieldModel, field.TypeJSON, value)
|
||||
}
|
||||
if pu.mutation.ConfigValuesCleared() {
|
||||
_spec.ClearField(platform.FieldConfigValues, field.TypeBytes)
|
||||
if pu.mutation.ModelCleared() {
|
||||
_spec.ClearField(platform.FieldModel, field.TypeJSON)
|
||||
}
|
||||
if value, ok := pu.mutation.ConfigCue(); ok {
|
||||
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
|
||||
if value, ok := pu.mutation.Cue(); ok {
|
||||
_spec.SetField(platform.FieldCue, field.TypeBytes, value)
|
||||
}
|
||||
if pu.mutation.ConfigCueCleared() {
|
||||
_spec.ClearField(platform.FieldConfigCue, field.TypeBytes)
|
||||
if pu.mutation.CueCleared() {
|
||||
_spec.ClearField(platform.FieldCue, field.TypeBytes)
|
||||
}
|
||||
if value, ok := pu.mutation.ConfigDefinition(); ok {
|
||||
_spec.SetField(platform.FieldConfigDefinition, field.TypeString, value)
|
||||
if value, ok := pu.mutation.CueDefinition(); ok {
|
||||
_spec.SetField(platform.FieldCueDefinition, field.TypeString, value)
|
||||
}
|
||||
if pu.mutation.ConfigDefinitionCleared() {
|
||||
_spec.ClearField(platform.FieldConfigDefinition, field.TypeString)
|
||||
if pu.mutation.CueDefinitionCleared() {
|
||||
_spec.ClearField(platform.FieldCueDefinition, field.TypeString)
|
||||
}
|
||||
if pu.mutation.CreatorCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
@@ -419,59 +420,59 @@ func (puo *PlatformUpdateOne) SetNillableCreatorID(u *uuid.UUID) *PlatformUpdate
|
||||
return puo
|
||||
}
|
||||
|
||||
// SetConfigForm sets the "config_form" field.
|
||||
func (puo *PlatformUpdateOne) SetConfigForm(b []byte) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigForm(b)
|
||||
// SetForm sets the "form" field.
|
||||
func (puo *PlatformUpdateOne) SetForm(h *holos.Form) *PlatformUpdateOne {
|
||||
puo.mutation.SetForm(h)
|
||||
return puo
|
||||
}
|
||||
|
||||
// ClearConfigForm clears the value of the "config_form" field.
|
||||
func (puo *PlatformUpdateOne) ClearConfigForm() *PlatformUpdateOne {
|
||||
puo.mutation.ClearConfigForm()
|
||||
// ClearForm clears the value of the "form" field.
|
||||
func (puo *PlatformUpdateOne) ClearForm() *PlatformUpdateOne {
|
||||
puo.mutation.ClearForm()
|
||||
return puo
|
||||
}
|
||||
|
||||
// SetConfigValues sets the "config_values" field.
|
||||
func (puo *PlatformUpdateOne) SetConfigValues(b []byte) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigValues(b)
|
||||
// SetModel sets the "model" field.
|
||||
func (puo *PlatformUpdateOne) SetModel(h *holos.Model) *PlatformUpdateOne {
|
||||
puo.mutation.SetModel(h)
|
||||
return puo
|
||||
}
|
||||
|
||||
// ClearConfigValues clears the value of the "config_values" field.
|
||||
func (puo *PlatformUpdateOne) ClearConfigValues() *PlatformUpdateOne {
|
||||
puo.mutation.ClearConfigValues()
|
||||
// ClearModel clears the value of the "model" field.
|
||||
func (puo *PlatformUpdateOne) ClearModel() *PlatformUpdateOne {
|
||||
puo.mutation.ClearModel()
|
||||
return puo
|
||||
}
|
||||
|
||||
// SetConfigCue sets the "config_cue" field.
|
||||
func (puo *PlatformUpdateOne) SetConfigCue(b []byte) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigCue(b)
|
||||
// SetCue sets the "cue" field.
|
||||
func (puo *PlatformUpdateOne) SetCue(b []byte) *PlatformUpdateOne {
|
||||
puo.mutation.SetCue(b)
|
||||
return puo
|
||||
}
|
||||
|
||||
// ClearConfigCue clears the value of the "config_cue" field.
|
||||
func (puo *PlatformUpdateOne) ClearConfigCue() *PlatformUpdateOne {
|
||||
puo.mutation.ClearConfigCue()
|
||||
// ClearCue clears the value of the "cue" field.
|
||||
func (puo *PlatformUpdateOne) ClearCue() *PlatformUpdateOne {
|
||||
puo.mutation.ClearCue()
|
||||
return puo
|
||||
}
|
||||
|
||||
// SetConfigDefinition sets the "config_definition" field.
|
||||
func (puo *PlatformUpdateOne) SetConfigDefinition(s string) *PlatformUpdateOne {
|
||||
puo.mutation.SetConfigDefinition(s)
|
||||
// SetCueDefinition sets the "cue_definition" field.
|
||||
func (puo *PlatformUpdateOne) SetCueDefinition(s string) *PlatformUpdateOne {
|
||||
puo.mutation.SetCueDefinition(s)
|
||||
return puo
|
||||
}
|
||||
|
||||
// SetNillableConfigDefinition sets the "config_definition" field if the given value is not nil.
|
||||
func (puo *PlatformUpdateOne) SetNillableConfigDefinition(s *string) *PlatformUpdateOne {
|
||||
// SetNillableCueDefinition sets the "cue_definition" field if the given value is not nil.
|
||||
func (puo *PlatformUpdateOne) SetNillableCueDefinition(s *string) *PlatformUpdateOne {
|
||||
if s != nil {
|
||||
puo.SetConfigDefinition(*s)
|
||||
puo.SetCueDefinition(*s)
|
||||
}
|
||||
return puo
|
||||
}
|
||||
|
||||
// ClearConfigDefinition clears the value of the "config_definition" field.
|
||||
func (puo *PlatformUpdateOne) ClearConfigDefinition() *PlatformUpdateOne {
|
||||
puo.mutation.ClearConfigDefinition()
|
||||
// ClearCueDefinition clears the value of the "cue_definition" field.
|
||||
func (puo *PlatformUpdateOne) ClearCueDefinition() *PlatformUpdateOne {
|
||||
puo.mutation.ClearCueDefinition()
|
||||
return puo
|
||||
}
|
||||
|
||||
@@ -611,29 +612,29 @@ func (puo *PlatformUpdateOne) sqlSave(ctx context.Context) (_node *Platform, err
|
||||
if value, ok := puo.mutation.DisplayName(); ok {
|
||||
_spec.SetField(platform.FieldDisplayName, field.TypeString, value)
|
||||
}
|
||||
if value, ok := puo.mutation.ConfigForm(); ok {
|
||||
_spec.SetField(platform.FieldConfigForm, field.TypeBytes, value)
|
||||
if value, ok := puo.mutation.Form(); ok {
|
||||
_spec.SetField(platform.FieldForm, field.TypeJSON, value)
|
||||
}
|
||||
if puo.mutation.ConfigFormCleared() {
|
||||
_spec.ClearField(platform.FieldConfigForm, field.TypeBytes)
|
||||
if puo.mutation.FormCleared() {
|
||||
_spec.ClearField(platform.FieldForm, field.TypeJSON)
|
||||
}
|
||||
if value, ok := puo.mutation.ConfigValues(); ok {
|
||||
_spec.SetField(platform.FieldConfigValues, field.TypeBytes, value)
|
||||
if value, ok := puo.mutation.Model(); ok {
|
||||
_spec.SetField(platform.FieldModel, field.TypeJSON, value)
|
||||
}
|
||||
if puo.mutation.ConfigValuesCleared() {
|
||||
_spec.ClearField(platform.FieldConfigValues, field.TypeBytes)
|
||||
if puo.mutation.ModelCleared() {
|
||||
_spec.ClearField(platform.FieldModel, field.TypeJSON)
|
||||
}
|
||||
if value, ok := puo.mutation.ConfigCue(); ok {
|
||||
_spec.SetField(platform.FieldConfigCue, field.TypeBytes, value)
|
||||
if value, ok := puo.mutation.Cue(); ok {
|
||||
_spec.SetField(platform.FieldCue, field.TypeBytes, value)
|
||||
}
|
||||
if puo.mutation.ConfigCueCleared() {
|
||||
_spec.ClearField(platform.FieldConfigCue, field.TypeBytes)
|
||||
if puo.mutation.CueCleared() {
|
||||
_spec.ClearField(platform.FieldCue, field.TypeBytes)
|
||||
}
|
||||
if value, ok := puo.mutation.ConfigDefinition(); ok {
|
||||
_spec.SetField(platform.FieldConfigDefinition, field.TypeString, value)
|
||||
if value, ok := puo.mutation.CueDefinition(); ok {
|
||||
_spec.SetField(platform.FieldCueDefinition, field.TypeString, value)
|
||||
}
|
||||
if puo.mutation.ConfigDefinitionCleared() {
|
||||
_spec.ClearField(platform.FieldConfigDefinition, field.TypeString)
|
||||
if puo.mutation.CueDefinitionCleared() {
|
||||
_spec.ClearField(platform.FieldCueDefinition, field.TypeString)
|
||||
}
|
||||
if puo.mutation.CreatorCleared() {
|
||||
edge := &sqlgraph.EdgeSpec{
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"entgo.io/ent/schema/field"
|
||||
"entgo.io/ent/schema/index"
|
||||
"github.com/gofrs/uuid"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
type Platform struct {
|
||||
@@ -25,16 +26,16 @@ func (Platform) Fields() []ent.Field {
|
||||
field.String("name").NotEmpty(),
|
||||
field.String("display_name"),
|
||||
field.UUID("creator_id", uuid.UUID{}),
|
||||
field.Bytes("config_form").
|
||||
field.JSON("form", &holos.Form{}).
|
||||
Optional().
|
||||
Comment("Opaque JSON bytes representing the platform config form."),
|
||||
field.Bytes("config_values").
|
||||
Comment("JSON representation of FormlyFormConfig[] refer to https://github.com/holos-run/holos/issues/161"),
|
||||
field.JSON("model", &holos.Model{}).
|
||||
Optional().
|
||||
Comment("Opaque JSON bytes representing the platform config values."),
|
||||
field.Bytes("config_cue").
|
||||
Comment("JSON representation of the form model which holds user input values refer to https://github.com/holos-run/holos/issues/161"),
|
||||
field.Bytes("cue").
|
||||
Optional().
|
||||
Comment("Opaque bytes representing the CUE definition of the config struct."),
|
||||
field.String("config_definition").
|
||||
Comment("CUE definition to vet the model against e.g. #PlatformConfig"),
|
||||
field.String("cue_definition").
|
||||
Optional().
|
||||
Comment("The definition name to vet config_values against config_cue e.g. '#PlatformSpec'"),
|
||||
}
|
||||
|
||||
47
internal/frontend/holos/.eslintrc.json
Normal file
47
internal/frontend/holos/.eslintrc.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"projects/**/*"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "attribute",
|
||||
"prefix": "app",
|
||||
"style": "camelCase"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"type": "element",
|
||||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.html"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/recommended",
|
||||
"plugin:@angular-eslint/template/accessibility"
|
||||
],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
3
internal/frontend/holos/.gitignore
vendored
3
internal/frontend/holos/.gitignore
vendored
@@ -40,3 +40,6 @@ testem.log
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# NX?
|
||||
/.nx/
|
||||
|
||||
@@ -100,8 +100,22 @@
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.html"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"schematicCollections": [
|
||||
"@angular-eslint/schematics"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
2137
internal/frontend/holos/package-lock.json
generated
2137
internal/frontend/holos/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,8 @@
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
"test": "ng test",
|
||||
"lint": "ng lint"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@@ -32,6 +33,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.3.4",
|
||||
"@angular-eslint/builder": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin": "17.3.0",
|
||||
"@angular-eslint/eslint-plugin-template": "17.3.0",
|
||||
"@angular-eslint/schematics": "17.3.0",
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "^17.3.4",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@bufbuild/buf": "^1.31.0",
|
||||
@@ -40,6 +46,9 @@
|
||||
"@connectrpc/protoc-gen-connect-query": "^1.3.1",
|
||||
"@ngx-formly/schematics": "^6.3.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.2.0",
|
||||
"@typescript-eslint/parser": "7.2.0",
|
||||
"eslint": "^8.57.0",
|
||||
"jasmine-core": "~5.1.0",
|
||||
"karma": "~6.4.0",
|
||||
"karma-chrome-launcher": "~3.2.0",
|
||||
@@ -48,4 +57,4 @@
|
||||
"karma-jasmine-html-reporter": "~2.1.0",
|
||||
"typescript": "~5.4.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import { provideClient } from "../connect/client.provider";
|
||||
import { UserService } from './gen/holos/v1alpha1/user_connect';
|
||||
import { OrganizationService } from './gen/holos/v1alpha1/organization_connect';
|
||||
import { PlatformService } from './gen/holos/v1alpha1/platform_connect';
|
||||
import { HolosPanelWrapperComponent } from '../wrappers/holos-panel-wrapper/holos-panel-wrapper.component';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
@@ -22,7 +23,9 @@ export const appConfig: ApplicationConfig = {
|
||||
ConnectModule.forRoot({
|
||||
baseUrl: window.location.origin
|
||||
}),
|
||||
FormlyModule.forRoot(),
|
||||
FormlyModule.forRoot({
|
||||
wrappers: [{ name: 'holos-panel', component: HolosPanelWrapperComponent }],
|
||||
}),
|
||||
),
|
||||
]
|
||||
};
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/organization.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { CreateCallerOrganizationRequest, GetCallerOrganizationsRequest, GetCallerOrganizationsResponse } from "./organization_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.GetCallerOrganizations
|
||||
*/
|
||||
export const getCallerOrganizations = {
|
||||
localName: "getCallerOrganizations",
|
||||
name: "GetCallerOrganizations",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetCallerOrganizationsRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.OrganizationService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.CreateCallerOrganization
|
||||
*/
|
||||
export const createCallerOrganization = {
|
||||
localName: "createCallerOrganization",
|
||||
name: "CreateCallerOrganization",
|
||||
kind: MethodKind.Unary,
|
||||
I: CreateCallerOrganizationRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.OrganizationService"
|
||||
}
|
||||
} as const;
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { CreateCallerOrganizationRequest, GetCallerOrganizationsRequest, GetCallerOrganizationsResponse } from "./organization_pb.js";
|
||||
import { CreateCallerOrganizationRequest, CreateCallerOrganizationResponse, ListCallerOrganizationsRequest, ListCallerOrganizationsResponse } from "./organization_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
@@ -13,12 +13,12 @@ export const OrganizationService = {
|
||||
typeName: "holos.v1alpha1.OrganizationService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.GetCallerOrganizations
|
||||
* @generated from rpc holos.v1alpha1.OrganizationService.ListCallerOrganizations
|
||||
*/
|
||||
getCallerOrganizations: {
|
||||
name: "GetCallerOrganizations",
|
||||
I: GetCallerOrganizationsRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
listCallerOrganizations: {
|
||||
name: "ListCallerOrganizations",
|
||||
I: ListCallerOrganizationsRequest,
|
||||
O: ListCallerOrganizationsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ export const OrganizationService = {
|
||||
createCallerOrganization: {
|
||||
name: "CreateCallerOrganization",
|
||||
I: CreateCallerOrganizationRequest,
|
||||
O: GetCallerOrganizationsResponse,
|
||||
O: CreateCallerOrganizationResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -72,40 +72,40 @@ export class Organization extends Message<Organization> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetCallerOrganizationsRequest
|
||||
* @generated from message holos.v1alpha1.ListCallerOrganizationsRequest
|
||||
*/
|
||||
export class GetCallerOrganizationsRequest extends Message<GetCallerOrganizationsRequest> {
|
||||
constructor(data?: PartialMessage<GetCallerOrganizationsRequest>) {
|
||||
export class ListCallerOrganizationsRequest extends Message<ListCallerOrganizationsRequest> {
|
||||
constructor(data?: PartialMessage<ListCallerOrganizationsRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerOrganizationsRequest";
|
||||
static readonly typeName = "holos.v1alpha1.ListCallerOrganizationsRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerOrganizationsRequest {
|
||||
return new GetCallerOrganizationsRequest().fromBinary(bytes, options);
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ListCallerOrganizationsRequest {
|
||||
return new ListCallerOrganizationsRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerOrganizationsRequest {
|
||||
return new GetCallerOrganizationsRequest().fromJson(jsonValue, options);
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ListCallerOrganizationsRequest {
|
||||
return new ListCallerOrganizationsRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerOrganizationsRequest {
|
||||
return new GetCallerOrganizationsRequest().fromJsonString(jsonString, options);
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ListCallerOrganizationsRequest {
|
||||
return new ListCallerOrganizationsRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerOrganizationsRequest | PlainMessage<GetCallerOrganizationsRequest> | undefined, b: GetCallerOrganizationsRequest | PlainMessage<GetCallerOrganizationsRequest> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerOrganizationsRequest, a, b);
|
||||
static equals(a: ListCallerOrganizationsRequest | PlainMessage<ListCallerOrganizationsRequest> | undefined, b: ListCallerOrganizationsRequest | PlainMessage<ListCallerOrganizationsRequest> | undefined): boolean {
|
||||
return proto3.util.equals(ListCallerOrganizationsRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.GetCallerOrganizationsResponse
|
||||
* @generated from message holos.v1alpha1.ListCallerOrganizationsResponse
|
||||
*/
|
||||
export class GetCallerOrganizationsResponse extends Message<GetCallerOrganizationsResponse> {
|
||||
export class ListCallerOrganizationsResponse extends Message<ListCallerOrganizationsResponse> {
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.User user = 1;
|
||||
*/
|
||||
@@ -116,32 +116,32 @@ export class GetCallerOrganizationsResponse extends Message<GetCallerOrganizatio
|
||||
*/
|
||||
organizations: Organization[] = [];
|
||||
|
||||
constructor(data?: PartialMessage<GetCallerOrganizationsResponse>) {
|
||||
constructor(data?: PartialMessage<ListCallerOrganizationsResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.GetCallerOrganizationsResponse";
|
||||
static readonly typeName = "holos.v1alpha1.ListCallerOrganizationsResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
{ no: 2, name: "organizations", kind: "message", T: Organization, repeated: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): GetCallerOrganizationsResponse {
|
||||
return new GetCallerOrganizationsResponse().fromBinary(bytes, options);
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): ListCallerOrganizationsResponse {
|
||||
return new ListCallerOrganizationsResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): GetCallerOrganizationsResponse {
|
||||
return new GetCallerOrganizationsResponse().fromJson(jsonValue, options);
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): ListCallerOrganizationsResponse {
|
||||
return new ListCallerOrganizationsResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): GetCallerOrganizationsResponse {
|
||||
return new GetCallerOrganizationsResponse().fromJsonString(jsonString, options);
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): ListCallerOrganizationsResponse {
|
||||
return new ListCallerOrganizationsResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: GetCallerOrganizationsResponse | PlainMessage<GetCallerOrganizationsResponse> | undefined, b: GetCallerOrganizationsResponse | PlainMessage<GetCallerOrganizationsResponse> | undefined): boolean {
|
||||
return proto3.util.equals(GetCallerOrganizationsResponse, a, b);
|
||||
static equals(a: ListCallerOrganizationsResponse | PlainMessage<ListCallerOrganizationsResponse> | undefined, b: ListCallerOrganizationsResponse | PlainMessage<ListCallerOrganizationsResponse> | undefined): boolean {
|
||||
return proto3.util.equals(ListCallerOrganizationsResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,3 +176,46 @@ export class CreateCallerOrganizationRequest extends Message<CreateCallerOrganiz
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.CreateCallerOrganizationResponse
|
||||
*/
|
||||
export class CreateCallerOrganizationResponse extends Message<CreateCallerOrganizationResponse> {
|
||||
/**
|
||||
* @generated from field: holos.v1alpha1.User user = 1;
|
||||
*/
|
||||
user?: User;
|
||||
|
||||
/**
|
||||
* @generated from field: repeated holos.v1alpha1.Organization organizations = 2;
|
||||
*/
|
||||
organizations: Organization[] = [];
|
||||
|
||||
constructor(data?: PartialMessage<CreateCallerOrganizationResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.CreateCallerOrganizationResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "user", kind: "message", T: User },
|
||||
{ no: 2, name: "organizations", kind: "message", T: Organization, repeated: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreateCallerOrganizationResponse {
|
||||
return new CreateCallerOrganizationResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): CreateCallerOrganizationResponse {
|
||||
return new CreateCallerOrganizationResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): CreateCallerOrganizationResponse {
|
||||
return new CreateCallerOrganizationResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: CreateCallerOrganizationResponse | PlainMessage<CreateCallerOrganizationResponse> | undefined, b: CreateCallerOrganizationResponse | PlainMessage<CreateCallerOrganizationResponse> | undefined): boolean {
|
||||
return proto3.util.equals(CreateCallerOrganizationResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/platform.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { AddPlatformRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
|
||||
*/
|
||||
export const getPlatforms = {
|
||||
localName: "getPlatforms",
|
||||
name: "GetPlatforms",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetPlatformsRequest,
|
||||
O: GetPlatformsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.AddPlatform
|
||||
*/
|
||||
export const addPlatform = {
|
||||
localName: "addPlatform",
|
||||
name: "AddPlatform",
|
||||
kind: MethodKind.Unary,
|
||||
I: AddPlatformRequest,
|
||||
O: GetPlatformsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatform
|
||||
*/
|
||||
export const getPlatform = {
|
||||
localName: "getPlatform",
|
||||
name: "GetPlatform",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetPlatformRequest,
|
||||
O: GetPlatformResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.PlatformService"
|
||||
}
|
||||
} as const;
|
||||
@@ -3,7 +3,7 @@
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { AddPlatformRequest, GetPlatformRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse } from "./platform_pb.js";
|
||||
import { AddPlatformRequest, AddPlatformResponse, GetFormRequest, GetFormResponse, GetModelRequest, GetModelResponse, GetPlatformRequest, GetPlatformResponse, ListPlatformsRequest, ListPlatformsResponse, PutFormRequest, PutFormResponse, PutModelRequest, PutModelResponse } from "./platform_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
@@ -12,22 +12,13 @@ import { MethodKind } from "@bufbuild/protobuf";
|
||||
export const PlatformService = {
|
||||
typeName: "holos.v1alpha1.PlatformService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetPlatforms
|
||||
*/
|
||||
getPlatforms: {
|
||||
name: "GetPlatforms",
|
||||
I: GetPlatformsRequest,
|
||||
O: GetPlatformsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.AddPlatform
|
||||
*/
|
||||
addPlatform: {
|
||||
name: "AddPlatform",
|
||||
I: AddPlatformRequest,
|
||||
O: GetPlatformsResponse,
|
||||
O: AddPlatformResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
@@ -39,6 +30,51 @@ export const PlatformService = {
|
||||
O: GetPlatformResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.ListPlatforms
|
||||
*/
|
||||
listPlatforms: {
|
||||
name: "ListPlatforms",
|
||||
I: ListPlatformsRequest,
|
||||
O: ListPlatformsResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetForm
|
||||
*/
|
||||
getForm: {
|
||||
name: "GetForm",
|
||||
I: GetFormRequest,
|
||||
O: GetFormResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.PutForm
|
||||
*/
|
||||
putForm: {
|
||||
name: "PutForm",
|
||||
I: PutFormRequest,
|
||||
O: PutFormResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.GetModel
|
||||
*/
|
||||
getModel: {
|
||||
name: "GetModel",
|
||||
I: GetModelRequest,
|
||||
O: GetModelResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.PlatformService.PutModel
|
||||
*/
|
||||
putModel: {
|
||||
name: "PutModel",
|
||||
I: PutModelRequest,
|
||||
O: PutModelResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
// @generated by protoc-gen-connect-es v1.4.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { DropTablesRequest, DropTablesResponse, SeedDatabaseRequest, SeedDatabaseResponse } from "./system_pb.js";
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from service holos.v1alpha1.SystemService
|
||||
*/
|
||||
export const SystemService = {
|
||||
typeName: "holos.v1alpha1.SystemService",
|
||||
methods: {
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.SystemService.SeedDatabase
|
||||
*/
|
||||
seedDatabase: {
|
||||
name: "SeedDatabase",
|
||||
I: SeedDatabaseRequest,
|
||||
O: SeedDatabaseResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.SystemService.DropTables
|
||||
*/
|
||||
dropTables: {
|
||||
name: "DropTables",
|
||||
I: DropTablesRequest,
|
||||
O: DropTablesResponse,
|
||||
kind: MethodKind.Unary,
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
132
internal/frontend/holos/src/app/gen/holos/v1alpha1/system_pb.ts
Normal file
132
internal/frontend/holos/src/app/gen/holos/v1alpha1/system_pb.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/system.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3 } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.SeedDatabaseRequest
|
||||
*/
|
||||
export class SeedDatabaseRequest extends Message<SeedDatabaseRequest> {
|
||||
constructor(data?: PartialMessage<SeedDatabaseRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.SeedDatabaseRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): SeedDatabaseRequest {
|
||||
return new SeedDatabaseRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): SeedDatabaseRequest {
|
||||
return new SeedDatabaseRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): SeedDatabaseRequest {
|
||||
return new SeedDatabaseRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: SeedDatabaseRequest | PlainMessage<SeedDatabaseRequest> | undefined, b: SeedDatabaseRequest | PlainMessage<SeedDatabaseRequest> | undefined): boolean {
|
||||
return proto3.util.equals(SeedDatabaseRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.SeedDatabaseResponse
|
||||
*/
|
||||
export class SeedDatabaseResponse extends Message<SeedDatabaseResponse> {
|
||||
constructor(data?: PartialMessage<SeedDatabaseResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.SeedDatabaseResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): SeedDatabaseResponse {
|
||||
return new SeedDatabaseResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): SeedDatabaseResponse {
|
||||
return new SeedDatabaseResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): SeedDatabaseResponse {
|
||||
return new SeedDatabaseResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: SeedDatabaseResponse | PlainMessage<SeedDatabaseResponse> | undefined, b: SeedDatabaseResponse | PlainMessage<SeedDatabaseResponse> | undefined): boolean {
|
||||
return proto3.util.equals(SeedDatabaseResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.DropTablesRequest
|
||||
*/
|
||||
export class DropTablesRequest extends Message<DropTablesRequest> {
|
||||
constructor(data?: PartialMessage<DropTablesRequest>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.DropTablesRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): DropTablesRequest {
|
||||
return new DropTablesRequest().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): DropTablesRequest {
|
||||
return new DropTablesRequest().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): DropTablesRequest {
|
||||
return new DropTablesRequest().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: DropTablesRequest | PlainMessage<DropTablesRequest> | undefined, b: DropTablesRequest | PlainMessage<DropTablesRequest> | undefined): boolean {
|
||||
return proto3.util.equals(DropTablesRequest, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.v1alpha1.DropTablesResponse
|
||||
*/
|
||||
export class DropTablesResponse extends Message<DropTablesResponse> {
|
||||
constructor(data?: PartialMessage<DropTablesResponse>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.v1alpha1.DropTablesResponse";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): DropTablesResponse {
|
||||
return new DropTablesResponse().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): DropTablesResponse {
|
||||
return new DropTablesResponse().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): DropTablesResponse {
|
||||
return new DropTablesResponse().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: DropTablesResponse | PlainMessage<DropTablesResponse> | undefined, b: DropTablesResponse | PlainMessage<DropTablesResponse> | undefined): boolean {
|
||||
return proto3.util.equals(DropTablesResponse, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
// @generated by protoc-gen-connect-query v1.3.1 with parameter "target=ts"
|
||||
// @generated from file holos/v1alpha1/user.proto (package holos.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import { MethodKind } from "@bufbuild/protobuf";
|
||||
import { CreateCallerUserRequest, CreateCallerUserResponse, GetCallerClaimsRequest, GetCallerClaimsResponse, GetCallerUserRequest, GetCallerUserResponse } from "./user_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.GetCallerClaims
|
||||
*/
|
||||
export const getCallerClaims = {
|
||||
localName: "getCallerClaims",
|
||||
name: "GetCallerClaims",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetCallerClaimsRequest,
|
||||
O: GetCallerClaimsResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.UserService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.GetCallerUser
|
||||
*/
|
||||
export const getCallerUser = {
|
||||
localName: "getCallerUser",
|
||||
name: "GetCallerUser",
|
||||
kind: MethodKind.Unary,
|
||||
I: GetCallerUserRequest,
|
||||
O: GetCallerUserResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.UserService"
|
||||
}
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @generated from rpc holos.v1alpha1.UserService.CreateCallerUser
|
||||
*/
|
||||
export const createCallerUser = {
|
||||
localName: "createCallerUser",
|
||||
name: "CreateCallerUser",
|
||||
kind: MethodKind.Unary,
|
||||
I: CreateCallerUserRequest,
|
||||
O: CreateCallerUserResponse,
|
||||
service: {
|
||||
typeName: "holos.v1alpha1.UserService"
|
||||
}
|
||||
} as const;
|
||||
@@ -33,7 +33,8 @@
|
||||
</span>
|
||||
<app-profile-button [claims$]="claims$"></app-profile-button>
|
||||
</mat-toolbar>
|
||||
<!-- Add Content Here -->
|
||||
<router-outlet></router-outlet>
|
||||
<main class="main-content">
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
.mat-toolbar.mat-primary {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.toolbar-spacer {
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { OrganizationService as ConnectOrganizationService } from '../gen/holos/v1alpha1/organization_connect';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { Observable, switchMap, of, shareReplay, catchError, BehaviorSubject } from 'rxjs';
|
||||
import { GetCallerOrganizationsResponse, Organization } from '../gen/holos/v1alpha1/organization_pb';
|
||||
import { UserService } from './user.service';
|
||||
import { Code, ConnectError } from '@connectrpc/connect';
|
||||
import { BehaviorSubject, Observable, catchError, of, shareReplay, switchMap } from 'rxjs';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { OrganizationService as ConnectOrganizationService } from '../gen/holos/v1alpha1/organization_connect';
|
||||
import { ListCallerOrganizationsResponse, Organization } from '../gen/holos/v1alpha1/organization_pb';
|
||||
import { UserService } from './user.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class OrganizationService {
|
||||
private callerOrganizationsTrigger$ = new BehaviorSubject<void>(undefined);
|
||||
private callerOrganizations$: Observable<GetCallerOrganizationsResponse>;
|
||||
private callerOrganizations$: Observable<ListCallerOrganizationsResponse>;
|
||||
|
||||
private fetchCallerOrganizations(): Observable<GetCallerOrganizationsResponse> {
|
||||
return this.client.getCallerOrganizations({ request: {} }).pipe(
|
||||
private fetchCallerOrganizations(): Observable<ListCallerOrganizationsResponse> {
|
||||
return this.client.listCallerOrganizations({ request: {} }).pipe(
|
||||
switchMap(resp => {
|
||||
if (resp && resp.organizations.length > 0) {
|
||||
return of(resp)
|
||||
@@ -25,7 +25,7 @@ export class OrganizationService {
|
||||
if (err instanceof ConnectError) {
|
||||
if (err.code == Code.NotFound) {
|
||||
return this.userService.createUser().pipe(
|
||||
switchMap(user => this.client.createCallerOrganization({ request: {} }))
|
||||
switchMap(() => this.client.createCallerOrganization({ request: {} }))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +1,40 @@
|
||||
import { Inject, Injectable, inject } from '@angular/core';
|
||||
import { PlatformService as ConnectPlatformService } from '../gen/holos/v1alpha1/platform_connect';
|
||||
import { Observable, filter, of, switchMap } from 'rxjs';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { JsonValue, Struct, } from '@bufbuild/protobuf';
|
||||
import { Observable, of, switchMap } from 'rxjs';
|
||||
import { ObservableClient } from '../../connect/observable-client';
|
||||
import { GetPlatformFormRequest, GetPlatformResponse, GetPlatformsRequest, GetPlatformsResponse, Platform, PlatformForm } from '../gen/holos/v1alpha1/platform_pb';
|
||||
import { Organization } from '../gen/holos/v1alpha1/organization_pb';
|
||||
import { PlatformService as ConnectPlatformService } from '../gen/holos/v1alpha1/platform_connect';
|
||||
import { GetFormResponse, ListPlatformsRequest, Platform, PutModelRequest, PutModelResponse } from '../gen/holos/v1alpha1/platform_pb';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PlatformService {
|
||||
getPlatforms(org: Observable<Organization>): Observable<Platform[]> {
|
||||
listPlatforms(org: Observable<Organization>): Observable<Platform[]> {
|
||||
return org.pipe(
|
||||
switchMap(org => {
|
||||
const req = new GetPlatformsRequest({ orgId: org.id })
|
||||
return this.client.getPlatforms(req).pipe(
|
||||
const req = new ListPlatformsRequest({ orgId: org.id })
|
||||
return this.client.listPlatforms(req).pipe(
|
||||
switchMap(resp => { return of(resp.platforms) })
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
getPlatform(id: string): Observable<Platform | undefined> {
|
||||
return this.client.getPlatform({ platformId: id }).pipe(
|
||||
switchMap((resp) => { return of(resp.platform) }),
|
||||
)
|
||||
getForm(id: string): Observable<GetFormResponse> {
|
||||
return this.client.getForm({ platformId: id })
|
||||
}
|
||||
|
||||
putModel(id: string, model: JsonValue): Observable<PutModelResponse> {
|
||||
const req = new PutModelRequest({
|
||||
platformId: id,
|
||||
// "We recommend to use fromJson() to construct Struct literals" refer to
|
||||
// https://github.com/bufbuild/protobuf-es/blob/main/docs/runtime_api.md#struct
|
||||
model: Struct.fromJson(model),
|
||||
})
|
||||
return this.client.putModel(req)
|
||||
}
|
||||
|
||||
|
||||
constructor(@Inject(ConnectPlatformService) private client: ObservableClient<typeof ConnectPlatformService>) { }
|
||||
}
|
||||
|
||||
@@ -1,25 +1,28 @@
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Detail">
|
||||
<div class="grid-container">
|
||||
@if (platform$ | async; as platform) {
|
||||
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
|
||||
@for (section of platform.config?.form?.spec?.sections; track section.name) {
|
||||
<h2>{{section.displayName ? section.displayName : section.name }}</h2>
|
||||
<p>{{ section.description }}</p>
|
||||
<formly-form [form]="form" [fields]="section.fieldConfigs" [model]="model"></formly-form>
|
||||
}
|
||||
<p></p>
|
||||
<button type="submit" mat-flat-button color="primary">Submit</button>
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
</mat-tab>
|
||||
<div class="content-container">
|
||||
<mat-tab-group>
|
||||
<mat-tab label="Detail">
|
||||
<div class="grid-container">
|
||||
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
|
||||
<formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
|
||||
<button type="submit" mat-flat-button color="primary" [disabled]="!form.valid">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
||||
<mat-tab label="Raw">
|
||||
<div class="grid-container">
|
||||
@if (platform$ | async; as platform) {
|
||||
<pre>{{ platform | json }}</pre>
|
||||
}
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
<mat-tab label="Model">
|
||||
<div class="grid-container">
|
||||
<pre>{{ model | json }}</pre>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Form State">
|
||||
<div class="grid-container">
|
||||
<pre>{{ options.formState | json }}</pre>
|
||||
</div>
|
||||
</mat-tab>
|
||||
<mat-tab label="Fields">
|
||||
<div class="grid-container">
|
||||
<pre>{{ fields | json }}</pre>
|
||||
</div>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
|
||||
@@ -1,49 +1,90 @@
|
||||
import { Component, Input, inject } from '@angular/core';
|
||||
import { Observable, filter, shareReplay } from 'rxjs';
|
||||
import { PlatformService } from '../../services/platform.service';
|
||||
import { Platform } from '../../gen/holos/v1alpha1/platform_pb';
|
||||
import { MatTab, MatTabGroup } from '@angular/material/tabs';
|
||||
import { AsyncPipe, CommonModule } from '@angular/common';
|
||||
import { Component, Input, OnDestroy, inject } from '@angular/core';
|
||||
import { FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormlyMaterialModule } from '@ngx-formly/material';
|
||||
import { FormlyModule } from '@ngx-formly/core';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { MatDivider } from '@angular/material/divider';
|
||||
import { MatTab, MatTabGroup } from '@angular/material/tabs';
|
||||
import { JsonValue } from '@bufbuild/protobuf';
|
||||
import { FormlyFieldConfig, FormlyFormOptions, FormlyModule } from '@ngx-formly/core';
|
||||
import { FormlyMaterialModule } from '@ngx-formly/material';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { PlatformService } from '../../services/platform.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-platform-detail',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatTabGroup,
|
||||
MatTab,
|
||||
AsyncPipe,
|
||||
CommonModule,
|
||||
FormlyModule,
|
||||
ReactiveFormsModule,
|
||||
FormlyMaterialModule,
|
||||
FormlyModule,
|
||||
MatButton,
|
||||
MatDivider,
|
||||
MatTab,
|
||||
MatTabGroup,
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
templateUrl: './platform-detail.component.html',
|
||||
styleUrl: './platform-detail.component.scss'
|
||||
})
|
||||
export class PlatformDetailComponent {
|
||||
private service = inject(PlatformService);
|
||||
|
||||
platform$!: Observable<Platform>;
|
||||
export class PlatformDetailComponent implements OnDestroy {
|
||||
private platformService = inject(PlatformService);
|
||||
private platformId: string = "";
|
||||
|
||||
private destroy$: Subject<boolean> = new Subject<boolean>();
|
||||
form = new FormGroup({});
|
||||
model = {};
|
||||
fields: FormlyFieldConfig[] = [];
|
||||
model: JsonValue = {};
|
||||
// Use form state to store the model for nested forms
|
||||
// Refer to https://formly.dev/docs/examples/form-options/form-state/
|
||||
options: FormlyFormOptions = {
|
||||
formState: {
|
||||
model: this.model,
|
||||
},
|
||||
};
|
||||
|
||||
onSubmit(model: any) {
|
||||
console.log(model);
|
||||
private setModel(model: JsonValue) {
|
||||
if (model) {
|
||||
this.model = model
|
||||
this.options.formState.model = model
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit(model: JsonValue) {
|
||||
if (this.form.valid) {
|
||||
console.log(model)
|
||||
this.platformService
|
||||
.putModel(this.platformId, model)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(resp => {
|
||||
if (resp.model !== undefined) {
|
||||
this.setModel(resp.model.toJson())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@Input()
|
||||
set id(platformId: string) {
|
||||
this.platform$ = this.service.getPlatform(platformId).pipe(
|
||||
filter((platform): platform is Platform => platform !== undefined),
|
||||
shareReplay(1)
|
||||
)
|
||||
this.platformId = platformId;
|
||||
this.platformService
|
||||
.getForm(platformId)
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(resp => {
|
||||
if (resp.model !== undefined) {
|
||||
this.setModel(resp.model.toJson())
|
||||
}
|
||||
if (resp.fields !== undefined) {
|
||||
// NOTE: We could mix functions into the json data via mapped fields,
|
||||
// but consider carefully before doing so. Refer to
|
||||
// https://formly.dev/docs/examples/other/json-powered
|
||||
this.fields = resp.fields.map(field => field.toJson() as FormlyFieldConfig)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public ngOnDestroy(): void {
|
||||
this.destroy$.next(true);
|
||||
this.destroy$.complete();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<div class="grid-container">
|
||||
<mat-nav-list>
|
||||
<h3 mat-subheader>Platforms</h3>
|
||||
@for (platform of platforms$ | async; track platform.id) {
|
||||
<mat-list-item>
|
||||
<a [routerLink]="['/platform', platform.id]">
|
||||
{{ platform.displayName ? platform.displayName : platform.name }}
|
||||
</a>
|
||||
</mat-list-item>
|
||||
<p>Select a platform to manage.</p>
|
||||
@for (platform of (platforms$ | async); track platform.id) {
|
||||
<a mat-list-item [routerLink]="['/platform', platform.id]">
|
||||
{{ platform.displayName ? platform.displayName : platform.name }}
|
||||
</a>
|
||||
}
|
||||
</mat-nav-list>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Platform } from '../../gen/holos/v1alpha1/platform_pb';
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, OnInit, inject } from '@angular/core';
|
||||
import { MatListItem, MatNavList } from '@angular/material/list';
|
||||
import { Observable, filter } from 'rxjs';
|
||||
import { Organization } from '../../gen/holos/v1alpha1/organization_pb';
|
||||
@@ -21,16 +21,16 @@ import { RouterLink } from '@angular/router';
|
||||
templateUrl: './platforms.component.html',
|
||||
styleUrl: './platforms.component.scss'
|
||||
})
|
||||
export class PlatformsComponent {
|
||||
export class PlatformsComponent implements OnInit {
|
||||
private orgSvc = inject(OrganizationService);
|
||||
private platformSvc = inject(PlatformService);
|
||||
|
||||
org$!: Observable<Organization | undefined>;
|
||||
platforms$!: Observable<Platform[]>;
|
||||
|
||||
ngOnInit(): void {
|
||||
ngOnInit() {
|
||||
this.org$ = this.orgSvc.activeOrg();
|
||||
this.platforms$ = this.platformSvc.getPlatforms(this.org$.pipe(
|
||||
this.platforms$ = this.platformSvc.listPlatforms(this.org$.pipe(
|
||||
filter((org): org is Organization => org !== undefined)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<div class="card">
|
||||
<h2 class="card-header">{{ props.label }}</h2>
|
||||
<div class="card-body">
|
||||
<p>{{ props.description }}</p>
|
||||
<ng-container #fieldComponent></ng-container>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
.card {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HolosPanelWrapperComponent } from './holos-panel-wrapper.component';
|
||||
|
||||
describe('HolosPanelWrapperComponent', () => {
|
||||
let component: HolosPanelWrapperComponent;
|
||||
let fixture: ComponentFixture<HolosPanelWrapperComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [HolosPanelWrapperComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(HolosPanelWrapperComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { FieldWrapper } from '@ngx-formly/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-holos-panel-wrapper',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './holos-panel-wrapper.component.html',
|
||||
styleUrl: './holos-panel-wrapper.component.scss'
|
||||
})
|
||||
export class HolosPanelWrapperComponent extends FieldWrapper { }
|
||||
@@ -1,219 +0,0 @@
|
||||
// Package builder is responsible for building fully rendered kubernetes api
|
||||
// objects from various input directories. A directory may contain a platform
|
||||
// spec or a component spec.
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"cuelang.org/go/cue/build"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
KubernetesObjects = v1alpha1.KubernetesObjectsKind
|
||||
// Helm is the value of the kind field of holos build output indicating helm
|
||||
// values and helm command information.
|
||||
Helm = v1alpha1.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 = v1alpha1.KustomizeBuildKind
|
||||
)
|
||||
|
||||
// An Option configures a Builder
|
||||
type Option func(*config)
|
||||
|
||||
type config struct {
|
||||
args []string
|
||||
cluster string
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
cfg config
|
||||
}
|
||||
|
||||
// New returns a new *Builder configured by opts Option.
|
||||
func New(opts ...Option) *Builder {
|
||||
var cfg config
|
||||
for _, f := range opts {
|
||||
f(&cfg)
|
||||
}
|
||||
b := &Builder{cfg: cfg}
|
||||
return b
|
||||
}
|
||||
|
||||
// Entrypoints configures the leaf directories Builder builds.
|
||||
func Entrypoints(args []string) Option {
|
||||
return func(cfg *config) { cfg.args = args }
|
||||
}
|
||||
|
||||
// Cluster configures the cluster name for the holos component instance.
|
||||
func Cluster(name string) Option {
|
||||
return func(cfg *config) { cfg.cluster = name }
|
||||
}
|
||||
|
||||
// Cluster returns the cluster name of the component instance being built.
|
||||
func (b *Builder) Cluster() string {
|
||||
return b.cfg.cluster
|
||||
}
|
||||
|
||||
// Instances returns the cue build instances being built.
|
||||
func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
mod, err := b.findCueMod()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
dir := string(mod)
|
||||
|
||||
cfg := load.Config{Dir: dir}
|
||||
|
||||
// Make args relative to the module directory
|
||||
args := make([]string, len(b.cfg.args))
|
||||
for idx, path := range b.cfg.args {
|
||||
target, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not find absolute path: %w", err))
|
||||
}
|
||||
relPath, err := filepath.Rel(dir, target)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("invalid argument, must be relative to cue.mod: %w", err))
|
||||
}
|
||||
relPath = "./" + relPath
|
||||
args[idx] = relPath
|
||||
equiv := fmt.Sprintf("cue export --out yaml -t cluster=%v %v", b.Cluster(), relPath)
|
||||
log.Debug("cue: equivalent command: " + equiv)
|
||||
}
|
||||
|
||||
// Refer to https://github.com/cue-lang/cue/blob/v0.7.0/cmd/cue/cmd/common.go#L429
|
||||
cfg.Tags = append(cfg.Tags, "cluster="+b.Cluster())
|
||||
log.DebugContext(ctx, fmt.Sprintf("cue: tags %v", cfg.Tags))
|
||||
|
||||
return load.Instances(args, &cfg), nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context) (results []*v1alpha1.Result, err error) {
|
||||
results = make([]*v1alpha1.Result, 0, len(b.cfg.args))
|
||||
cueCtx := cuecontext.New()
|
||||
logger.FromContext(ctx).DebugContext(ctx, "cue: building instances")
|
||||
instances, err := b.Instances(ctx)
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
// Each CUE instance provides a BuildPlan
|
||||
for _, instance := range instances {
|
||||
var buildPlan v1alpha1.BuildPlan
|
||||
|
||||
log := logger.FromContext(ctx).With("dir", instance.Dir)
|
||||
if err := instance.Err; err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not load: %w", err))
|
||||
}
|
||||
log.DebugContext(ctx, "cue: building instance")
|
||||
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 build plan")
|
||||
// Hack to catch unknown fields https://github.com/holos-run/holos/issues/72
|
||||
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))
|
||||
decoder.DisallowUnknownFields()
|
||||
err = decoder.Decode(&buildPlan)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("invalid BuildPlan: %s: %w", instance.Dir, err))
|
||||
}
|
||||
|
||||
if err := buildPlan.Validate(); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not validate %s: %w", instance.Dir, err))
|
||||
}
|
||||
|
||||
if buildPlan.Spec.Disabled {
|
||||
log.DebugContext(ctx, "skipped: spec.disabled is true", "skipped", true)
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: concurrent renders
|
||||
for _, component := range buildPlan.Spec.Components.Resources {
|
||||
if result, err := component.Render(ctx, holos.InstancePath(instance.Dir)); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
}
|
||||
}
|
||||
for _, component := range buildPlan.Spec.Components.KubernetesObjectsList {
|
||||
if result, err := component.Render(ctx, holos.InstancePath(instance.Dir)); 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, holos.InstancePath(instance.Dir)); 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, holos.InstancePath(instance.Dir)); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// findCueMod returns the root module location containing the cue.mod file or
|
||||
// directory or an error if the builder arguments do not share a common root
|
||||
// module.
|
||||
func (b *Builder) findCueMod() (dir holos.PathCueMod, err error) {
|
||||
for _, origPath := range b.cfg.args {
|
||||
absPath, err := filepath.Abs(origPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
path := holos.PathCueMod(absPath)
|
||||
for {
|
||||
if _, err := os.Stat(filepath.Join(string(path), "cue.mod")); err == nil {
|
||||
if dir != "" && dir != path {
|
||||
return "", fmt.Errorf("multiple modules not supported: %v is not %v", dir, path)
|
||||
}
|
||||
dir = path
|
||||
break
|
||||
} else if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
parentPath := holos.PathCueMod(filepath.Dir(string(path)))
|
||||
if parentPath == path {
|
||||
return "", fmt.Errorf("no cue.mod from root to leaf: %v", origPath)
|
||||
}
|
||||
path = parentPath
|
||||
}
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
4
internal/platforms/bare/components/cluster.cue
Normal file
4
internal/platforms/bare/components/cluster.cue
Normal file
@@ -0,0 +1,4 @@
|
||||
package holos
|
||||
|
||||
// #ClusterName is the --cluster-name flag value provided by the holos cli.
|
||||
#ClusterName: string @tag(cluster, type=string)
|
||||
@@ -1,26 +1,20 @@
|
||||
package holos
|
||||
|
||||
import ( "encoding/yaml"
|
||||
import "encoding/yaml"
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
)
|
||||
let PLATFORM = {message: "TODO: Load the platform from the API."}
|
||||
|
||||
// The platform configmap is a simple component that manages a configmap named
|
||||
// platform in the default namespace. The purpose is to exercise end to end
|
||||
// validation of platform configuration values provided by the holos web ui to
|
||||
// each cluster in the platform.
|
||||
platform: #Platform & {metadata: name: "bare"}
|
||||
let PLATFORM = platform
|
||||
|
||||
// spec represents the output provided to holos
|
||||
spec: components: KubernetesObjectsList: [
|
||||
#KubernetesObjects & {
|
||||
// Provide a BuildPlan to the holos cli to render k8s api objects.
|
||||
v1.#BuildPlan & {
|
||||
spec: components: resources: platformConfigmap: {
|
||||
metadata: name: "platform-configmap"
|
||||
apiObjectMap: OBJECTS.apiObjectMap
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
// OBJECTS represents the kubernetes api objects to manage.
|
||||
let OBJECTS = #APIObjects & {
|
||||
let OBJECTS = v1.#APIObjects & {
|
||||
apiObjects: ConfigMap: platform: {
|
||||
metadata: {
|
||||
name: "platform"
|
||||
|
||||
@@ -1,43 +1,358 @@
|
||||
package forms
|
||||
|
||||
import formsv1 "github.com/holos-run/forms/v1alpha1"
|
||||
import v1 "github.com/holos-run/holos/v1alpha1"
|
||||
|
||||
let Platform = formsv1.#Platform & {
|
||||
name: "bare"
|
||||
displayName: "Bare Platform"
|
||||
// Provides a concrete v1.#Form
|
||||
FormBuilder.Output
|
||||
|
||||
sections: org: {
|
||||
let FormBuilder = v1.#FormBuilder & {
|
||||
Sections: org: {
|
||||
displayName: "Organization"
|
||||
description: "Organization config values are used to derive more specific configuration values throughout the platform."
|
||||
|
||||
fieldConfigs: {
|
||||
// platform.org.name
|
||||
name: props: {
|
||||
label: "Name"
|
||||
placeholder: "example"
|
||||
description: "DNS label, e.g. 'example'"
|
||||
// platform.spec.config.user.sections.org.fields.name
|
||||
name: {
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Name"
|
||||
// placeholder: "example" placeholder cannot be used with validation?
|
||||
description: "DNS label, e.g. 'example'"
|
||||
pattern: "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$"
|
||||
minLength: 3
|
||||
maxLength: 30
|
||||
required: true
|
||||
}
|
||||
validation: messages: {
|
||||
pattern: "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
}
|
||||
}
|
||||
// platform.org.domain
|
||||
domain: props: {
|
||||
label: "Domain"
|
||||
placeholder: "example.com"
|
||||
description: "DNS domain, e.g. 'example.com'"
|
||||
|
||||
// platform.spec.config.user.sections.org.fields.domain
|
||||
domain: {
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Domain"
|
||||
placeholder: "example.com"
|
||||
minLength: 3
|
||||
maxLength: 100
|
||||
description: "DNS domain, e.g. 'example.com'"
|
||||
required: true
|
||||
}
|
||||
}
|
||||
// platform.org.displayName
|
||||
displayName: props: {
|
||||
label: "Display Name"
|
||||
placeholder: "Example Organization"
|
||||
description: "Display name, e.g. 'Example Organization'"
|
||||
// platform.spec.config.user.sections.org.fields.displayName
|
||||
displayName: {
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Display Name"
|
||||
placeholder: "Example Organization"
|
||||
description: "Display name, e.g. 'Example Organization'"
|
||||
maxLength: 100
|
||||
required: true
|
||||
}
|
||||
}
|
||||
// platform.org.contactEmail
|
||||
contactEmail: props: {
|
||||
label: "Contact Email"
|
||||
placeholder: "platform-team@example.com"
|
||||
description: "Technical contact email address"
|
||||
// platform.spec.config.user.sections.org.fields.contactEmail
|
||||
contactEmail: {
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Contact Email"
|
||||
placeholder: "platform-team@example.com"
|
||||
description: "Technical contact email address"
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sections: cloud: {
|
||||
displayName: "Cloud Providers"
|
||||
description: "Select the services that provide resources for the platform."
|
||||
|
||||
fieldConfigs: {
|
||||
providers: {
|
||||
// https://formly.dev/docs/api/ui/material/select/
|
||||
type: "select"
|
||||
props: {
|
||||
label: "Select Providers"
|
||||
description: "Select the cloud providers the platform builds upon."
|
||||
multiple: true
|
||||
selectAllOption: "Select All"
|
||||
options: [
|
||||
{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},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sections: aws: {
|
||||
displayName: "Amazon Web Services"
|
||||
description: "Provide the information necessary for Holos to manage AWS resources to provide the platform."
|
||||
|
||||
expressions: hide: "!\(AWSSelected)"
|
||||
|
||||
fieldConfigs: {
|
||||
primaryRoleARN: {
|
||||
// https://formly.dev/docs/api/ui/material/input
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Holos Admin Role ARN"
|
||||
description: "Enter the AWS Role ARN Holos will use to bootstrap resources. For example, arn:aws:iam::123456789012:role/HolosAdminAccess"
|
||||
pattern: "^arn:.*"
|
||||
minLength: 4
|
||||
required: true
|
||||
}
|
||||
validation: messages: {
|
||||
pattern: "Must be a valid ARN. Refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html"
|
||||
}
|
||||
}
|
||||
|
||||
regions: {
|
||||
// https://formly.dev/docs/api/ui/material/select/
|
||||
type: "select"
|
||||
props: {
|
||||
label: "Select Regions"
|
||||
description: "Select the AWS regions this platform operates in."
|
||||
multiple: true
|
||||
required: true
|
||||
selectAllOption: "Select All"
|
||||
options: AWSRegions
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sections: gcp: {
|
||||
displayName: "Google Cloud Platform"
|
||||
description: "Use this form to configure platform level GCP settings."
|
||||
|
||||
expressions: hide: "!\(GCPSelected)"
|
||||
|
||||
fieldConfigs: {
|
||||
regions: {
|
||||
// https://formly.dev/docs/api/ui/material/select/
|
||||
type: "select"
|
||||
props: {
|
||||
label: "Select Regions"
|
||||
description: "Select the GCP regions this platform operates in."
|
||||
multiple: true
|
||||
selectAllOption: "Select All"
|
||||
// gcloud compute regions list --format=json | jq '.[] | {value: .name, label: .description}' regions.json | jq -s | cue export --out cue
|
||||
options: GCPRegions
|
||||
}
|
||||
}
|
||||
|
||||
gcpProjectID: {
|
||||
// https://formly.dev/docs/api/ui/material/input
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Project ID"
|
||||
description: "Enter the project id where the provisioner cluster resides."
|
||||
pattern: "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$"
|
||||
minLength: 6
|
||||
maxLength: 30
|
||||
required: true
|
||||
}
|
||||
validation: messages: {
|
||||
pattern: "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
}
|
||||
}
|
||||
|
||||
gcpProjectNumber: {
|
||||
// https://formly.dev/docs/api/ui/material/input
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Project Number"
|
||||
// note type number here
|
||||
type: "number"
|
||||
description: "Enter the project number where the provisioner cluster resides."
|
||||
pattern: "^[0-9]+$"
|
||||
required: true
|
||||
}
|
||||
validation: messages: {
|
||||
pattern: "Must be a valid project number."
|
||||
}
|
||||
}
|
||||
|
||||
provisionerCABundle: {
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Provisioner CA Bundle"
|
||||
description: "Enter the provisioner cluster ca bundle. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.certificate-authority-data}'"
|
||||
pattern: "^[0-9a-zA-Z]+=*$"
|
||||
required: true
|
||||
}
|
||||
validation: messages: {
|
||||
pattern: "Must be a base64 encoded pem encoded certificate bundle."
|
||||
}
|
||||
}
|
||||
|
||||
provisionerURL: {
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Provisioner URL"
|
||||
description: "Enter the URL of the provisioner cluster API endpoint. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.server}'"
|
||||
pattern: "^https://.*$"
|
||||
required: true
|
||||
}
|
||||
validation: messages: {
|
||||
pattern: "Must be a https:// URL."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sections: cloudflare: {
|
||||
displayName: "Cloudflare"
|
||||
description: "Cloudflare is primarily used for DNS automation."
|
||||
|
||||
expressions: hide: "!" + CloudflareSelected
|
||||
|
||||
fieldConfigs: {
|
||||
email: {
|
||||
// https://formly.dev/docs/api/ui/material/input
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Account Email"
|
||||
description: "Enter the Cloudflare email address to manage DNS"
|
||||
minLength: 3
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sections: github: {
|
||||
displayName: "GitHub"
|
||||
description: "GitHub is primarily used to host Git repositories and execute Actions workflows."
|
||||
|
||||
expressions: hide: "!\(GitHubSelected)"
|
||||
|
||||
fieldConfigs: {
|
||||
primaryOrg: {
|
||||
// https://formly.dev/docs/api/ui/material/input
|
||||
type: "input"
|
||||
props: {
|
||||
label: "Organization"
|
||||
description: "Enter the primary GitHub organization associed with the platform."
|
||||
pattern: "^(?!-)(?!.*--)([a-zA-Z0-9]|-){1,39}$"
|
||||
minLength: 1
|
||||
maxLength: 39
|
||||
required: true
|
||||
}
|
||||
validation: messages: {
|
||||
pattern: "All characters must be either a hyphen or alphanumeric. Cannot start with a hyphen. Cannot include consecutive hyphens."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Sections: backups: {
|
||||
displayName: "Backups"
|
||||
description: "Configure platform level data backup settings. Requires AWS."
|
||||
|
||||
fieldConfigs: {
|
||||
s3bucket: {
|
||||
// https://formly.dev/docs/api/ui/material/input
|
||||
type: "select"
|
||||
props: {
|
||||
label: "S3 Bucket Region"
|
||||
description: "Select the S3 Bucket Region."
|
||||
multiple: true
|
||||
options: AWSRegions
|
||||
}
|
||||
expressions: {
|
||||
// Disable the control if AWS is not selected.
|
||||
"props.disabled": "!" + AWSSelected
|
||||
// Required if AWS is selected.
|
||||
"props.required": AWSSelected
|
||||
// Change the label depending on AWS
|
||||
"props.description": AWSSelected + " ? '\(props.description)' : 'Enable AWS in the Cloud Provider section to configure backups.'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Provide the output form fields
|
||||
Platform.Form
|
||||
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: "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"},
|
||||
]
|
||||
|
||||
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: "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: "cn-northwest-1", label: "Ningxia (cn-northwest-1)"},
|
||||
{value: "ap-southeast-3", label: "Jakarta (ap-southeast-3)"},
|
||||
]
|
||||
|
||||
let AWSSelected = "formState.model.cloud?.providers?.includes(\"aws\")"
|
||||
let GCPSelected = "formState.model.cloud?.providers?.includes(\"gcp\")"
|
||||
let GitHubSelected = "formState.model.cloud?.providers?.includes(\"github\")"
|
||||
let CloudflareSelected = "formState.model.cloud?.providers?.includes(\"cloudflare\")"
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package holos
|
||||
|
||||
import (
|
||||
h "github.com/holos-run/holos/api/v1alpha1"
|
||||
"encoding/yaml"
|
||||
)
|
||||
|
||||
// CUE provides a #BuildPlan to the holos cli. Holos requires the output of CUE
|
||||
// to conform to the #BuildPlan schema.
|
||||
{} & h.#BuildPlan
|
||||
|
||||
// #HolosComponent defines struct fields common to all holos component types.
|
||||
#HolosComponent: {
|
||||
h.#HolosComponent
|
||||
metadata: name: string
|
||||
_NameLengthConstraint: len(metadata.name) & >=1
|
||||
...
|
||||
}
|
||||
|
||||
#KubernetesObjects: #HolosComponent & h.#KubernetesObjects
|
||||
|
||||
// #HolosTypeMeta is similar to kubernetes api TypeMeta, but for holos api
|
||||
// objects such as the Platform config resource.
|
||||
#HolosTypeMeta: {
|
||||
kind: string @go(Kind)
|
||||
apiVersion: string @go(APIVersion)
|
||||
}
|
||||
|
||||
// #HolosObjectMeta is similar to kubernetes api ObjectMeta, but for holos api
|
||||
// objects.
|
||||
#HolosObjectMeta: {
|
||||
name: string @go(Name)
|
||||
labels: {[string]: string} @go(Labels,map[string]string)
|
||||
annotations: {[string]: string} @go(Annotations,map[string]string)
|
||||
}
|
||||
|
||||
// #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]: {
|
||||
kind: Kind
|
||||
...
|
||||
}
|
||||
}
|
||||
ConfigMap?: [Name=_]: #ConfigMap & {metadata: name: Name}
|
||||
}
|
||||
|
||||
// apiObjectMap holds the marshalled representation of apiObjects
|
||||
apiObjectMap: {
|
||||
for kind, v in apiObjects {
|
||||
"\(kind)": {
|
||||
for name, obj in v {
|
||||
"\(name)": yaml.Marshal(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package holos
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
_NamespaceObject: {
|
||||
metadata: name: string
|
||||
metadata: namespace: string
|
||||
metadata: labels: "app.holos.run/managed": "true"
|
||||
}
|
||||
|
||||
#ConfigMap: _NamespaceObject & corev1.#ConfigMap
|
||||
@@ -1,25 +0,0 @@
|
||||
package holos
|
||||
|
||||
// #Platform represents the user supplied platform configuration.
|
||||
#Platform: {
|
||||
#HolosTypeMeta
|
||||
kind: "Platform"
|
||||
apiVersion: "app.holos.run/v1alpha1"
|
||||
metadata: #HolosObjectMeta
|
||||
spec: #PlatformSpec
|
||||
holos: #Holos
|
||||
}
|
||||
|
||||
// #Holos represents the holos reserved field in the #Platform schema defined by the holos development team.
|
||||
#Holos: {
|
||||
// flags represents config values provided by holos command line flags.
|
||||
flags: {
|
||||
// cluster represents the holos render --cluster-name flag.
|
||||
cluster: string @tag(cluster, type=string)
|
||||
}
|
||||
}
|
||||
|
||||
// #PlatformSpec represents configuration values defined by the platform
|
||||
// designer. Config values are organized by section, then simple strings for
|
||||
// each section.
|
||||
#PlatformSpec: {[string]: {[string]: string | bool | [...string]}}
|
||||
@@ -1 +0,0 @@
|
||||
{"platform":{"spec":{"org":{"name":"ois"}}}}
|
||||
20
internal/platforms/bare/readme.md
Normal file
20
internal/platforms/bare/readme.md
Normal file
@@ -0,0 +1,20 @@
|
||||
Bare Platform
|
||||
|
||||
| Folder | Description |
|
||||
| - | - |
|
||||
| forms | Contains Platform and Project form and model definitions |
|
||||
| platform | Contains the Platform resource that defines how to render the configuration for all Platform Components |
|
||||
| components | Contains BuildPlan resources which define how to render individual Platform Components |
|
||||
|
||||
## Forms
|
||||
|
||||
To populate the form, the platform must already be created in the Web UI:
|
||||
|
||||
```bash
|
||||
platformId="018f36fb-e3ff-7f7f-a5d1-7ca2bf499e94"
|
||||
cue export ./forms/platform/ --out json \
|
||||
| jq '{platform_id: "'$platformId'", fields: .spec.fields}' \
|
||||
| grpcurl -H "x-oidc-id-token: $(holos token)" -d @ \
|
||||
app.dev.k2.holos.run:443 \
|
||||
holos.v1alpha1.PlatformService.PutForm
|
||||
```
|
||||
@@ -1 +0,0 @@
|
||||
package holos
|
||||
@@ -1,65 +0,0 @@
|
||||
package v1alpha1
|
||||
|
||||
#Platform: {
|
||||
name: string // short dns label name
|
||||
displayName: string // Display name
|
||||
description: string // Plaform description
|
||||
|
||||
sections: {[NAME=string]: #ConfigSection & {name: NAME}}
|
||||
|
||||
Form: {
|
||||
let Name = name
|
||||
apiVersion: "forms.holos.run/v1alpha1"
|
||||
kind: "PlatformForm"
|
||||
metadata: name: Name
|
||||
spec: #PlatformFormSpec
|
||||
}
|
||||
|
||||
let Sections = sections
|
||||
Form: spec: sections: [for s in Sections {s.output}]
|
||||
}
|
||||
|
||||
#PlatformFormSpec: {
|
||||
sections: [...#ConfigSectionOutput]
|
||||
}
|
||||
|
||||
// #ConfigSection represents a configuration section of the front end UI. For
|
||||
// example, Organization config values. The fields of the section map to form
|
||||
// input fields.
|
||||
#ConfigSection: {
|
||||
name: string // e.g. "org"
|
||||
displayName: string // e.g. "Organization"
|
||||
description: string
|
||||
fieldConfigs: {[NAME=string]: #FieldConfig & {key: NAME}}
|
||||
|
||||
let Name = name
|
||||
let DisplayName = displayName
|
||||
let Description = description
|
||||
let FieldConfigs = fieldConfigs
|
||||
|
||||
output: #ConfigSectionOutput & {
|
||||
name: Name
|
||||
displayName: DisplayName
|
||||
description: Description
|
||||
fieldConfigs: [for fc in FieldConfigs {fc}]
|
||||
}
|
||||
}
|
||||
|
||||
#ConfigSectionOutput: {
|
||||
name: string
|
||||
displayName: string
|
||||
description: string
|
||||
fieldConfigs: [...#FieldConfig]
|
||||
}
|
||||
|
||||
// Refer to https://formly.dev/docs/api/core#formlyfieldconfig
|
||||
#FieldConfig: {
|
||||
key: string
|
||||
type: "input"
|
||||
props: {
|
||||
label: string
|
||||
placeholder: string
|
||||
description: string
|
||||
required: *true | false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package v1alpha1
|
||||
|
||||
import "encoding/yaml"
|
||||
|
||||
import core "k8s.io/api/core/v1"
|
||||
|
||||
// #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]: core.#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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package v1alpha1
|
||||
|
||||
#BuildPlan: {
|
||||
apiVersion: #APIVersion
|
||||
kind: #BuildPlanKind
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package v1alpha1
|
||||
|
||||
// #Form represents a web app form to provide to the Holos API for display in
|
||||
// the web app. A form is implemented as an Formly FieldConfig array using
|
||||
// Angular Material form field components.
|
||||
#Form: {
|
||||
#TypeMeta
|
||||
apiVersion: #APIVersion
|
||||
kind: "Form"
|
||||
|
||||
spec: fields: [...#FieldConfig]
|
||||
}
|
||||
|
||||
// #FormBuilder provides a concrete #Form via the Output field.
|
||||
#FormBuilder: {
|
||||
Name: string
|
||||
Sections: {[NAME=string]: #FormSection & {name: NAME}}
|
||||
|
||||
Output: #Form & {
|
||||
spec: fields: [for s in Sections {s.wrapper}]
|
||||
}
|
||||
}
|
||||
|
||||
// #FormSection represents a configuration section of the front end UI. The
|
||||
// wrapper field provides a concrete #FieldConfig for the form section. The
|
||||
// fields of the section map to form input fields.
|
||||
// Refer to: to https://formly.dev/docs/examples/other/nested-formly-forms
|
||||
#FormSection: {
|
||||
name: string // e.g. "org"
|
||||
displayName: string // e.g. "Organization"
|
||||
description: string
|
||||
|
||||
expressions: {[string]: string}
|
||||
|
||||
fieldConfigs: {[NAME=string]: #FieldConfig & {key: NAME}}
|
||||
|
||||
let Description = description
|
||||
let Expressions = expressions
|
||||
|
||||
// Wrap the fields of the section into one FormlyFieldConfig
|
||||
wrapper: #FieldConfig & {
|
||||
key: name
|
||||
// See our custom wrappers registered in app.config.ts
|
||||
wrappers: ["holos-panel"]
|
||||
props: label: displayName
|
||||
props: description: Description
|
||||
for k, v in Expressions {
|
||||
expressions: "\(k)": v
|
||||
}
|
||||
|
||||
// Might need to initialize the default value for a fieldGroup
|
||||
// https://github.com/ngx-formly/ngx-formly/issues/3667
|
||||
fieldGroup: [for fc in fieldConfigs {fc}]
|
||||
}
|
||||
}
|
||||
|
||||
// #FieldConfig represents a Formly Field Config.
|
||||
// Refer to https://formly.dev/docs/api/core#formlyfieldconfig
|
||||
// Refer to https://formly.dev/docs/api/ui/material/select
|
||||
#FieldConfig: {
|
||||
key: string
|
||||
// type is optional, may be a nested form which has no type field
|
||||
type?: string | "input" | "select" | "checkbox"
|
||||
// For nested forms, refer: to https://formly.dev/docs/examples/other/nested-formly-forms
|
||||
wrappers?: [...string]
|
||||
// Refer to: https://formly.dev/docs/api/ui/material/select#formlyselectprops
|
||||
// and other input field select props.
|
||||
props: {
|
||||
#FormlySelectProps
|
||||
|
||||
label: string
|
||||
type?: string
|
||||
placeholder?: string
|
||||
description: string
|
||||
required?: *true | false
|
||||
pattern?: string
|
||||
minLength?: number
|
||||
maxLength?: number
|
||||
}
|
||||
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L49-L64
|
||||
// We support only the string form.
|
||||
validation?: {
|
||||
// Note, you can set messages for pattern, minLength, maxLength here.
|
||||
messages?: [string]: string
|
||||
}
|
||||
|
||||
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L66-L71
|
||||
// We do not support validators because they must be javascript functions, not data.
|
||||
validators?: "not supported"
|
||||
|
||||
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L115-L120
|
||||
expressions?: [string]: string
|
||||
hide?: true | false
|
||||
// Required to populate protobuf value.
|
||||
resetOnHide: *true | false
|
||||
defaultValue?: _
|
||||
className?: string
|
||||
fieldGroup?: [...#FieldConfig]
|
||||
focus?: true | *false
|
||||
modelOptions?: {
|
||||
debounce?: {
|
||||
default: number
|
||||
}
|
||||
updateOn?: "change" | "blur" | "submit"
|
||||
}
|
||||
}
|
||||
|
||||
// Refer to https://formly.dev/docs/api/ui/material/select#formlyselectprops
|
||||
#FormlySelectProps: {
|
||||
disableOptionCentering?: true | false
|
||||
multiple?: true | false
|
||||
panelClass?: string
|
||||
selectAllOption?: string
|
||||
typeaheadDebounceInterval?: number
|
||||
|
||||
options?: [...{value: string | number | bool, label: string, disabled?: true | *false}]
|
||||
|
||||
// These could be used to set different keys for value and label in the
|
||||
// options list, but we don't support that level of customization.
|
||||
// They're here for documentation purposes only.
|
||||
labelProp?: "label"
|
||||
valueProp?: "value"
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package v1alpha1
|
||||
|
||||
// #HolosComponent defines struct fields common to all holos platform
|
||||
// component types.
|
||||
#HolosComponent: {
|
||||
metadata: name: string
|
||||
_NameLengthConstraint: len(metadata.name) & >=1
|
||||
...
|
||||
}
|
||||
|
||||
// #KubernetesObjects is a Holos Component BuildPlan which has k8s api objects
|
||||
// embedded into the build plan itself.
|
||||
#KubernetesObjects: #HolosComponent & {
|
||||
apiVersion: #APIVersion
|
||||
kind: #KubernetesObjectsKind
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package v1alpha1
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
|
||||
#ConfigMap: corev1.#ConfigMap & {
|
||||
apiVersion: "v1"
|
||||
kind: "ConfigMap"
|
||||
...
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package v1alpha1
|
||||
@@ -0,0 +1,20 @@
|
||||
package v1alpha1
|
||||
|
||||
// #Platform represents a platform to manage. Holos manages a platform by
|
||||
// rendering platform components and applying the configuration to clusters as
|
||||
// defined by the platform resource.
|
||||
#Platform: {
|
||||
#TypeMeta
|
||||
apiVersion: #APIVersion
|
||||
kind: "Platform"
|
||||
metadata: #ObjectMeta
|
||||
spec: {
|
||||
// model represents the user defined platform model, which is produced and
|
||||
// defined by the user supplied form.
|
||||
model: {...}
|
||||
|
||||
// components represents components to manage in the platform, organized by
|
||||
// the kind of cluster the rendered configuration applies to.
|
||||
components: {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package v1alpha1
|
||||
@@ -0,0 +1,16 @@
|
||||
package v1alpha1
|
||||
|
||||
// #TypeMeta is similar to kubernetes api TypeMeta, but for holos api
|
||||
// objects such as the Platform config resource.
|
||||
#TypeMeta: {
|
||||
kind: string @go(Kind)
|
||||
apiVersion: string @go(APIVersion)
|
||||
}
|
||||
|
||||
// #ObjectMeta is similar to kubernetes api ObjectMeta, but for holos api
|
||||
// objects.
|
||||
#ObjectMeta: {
|
||||
name: string @go(Name)
|
||||
labels: {[string]: string} @go(Labels,map[string]string)
|
||||
annotations: {[string]: string} @go(Annotations,map[string]string)
|
||||
}
|
||||
@@ -9,32 +9,37 @@ package handler
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
)
|
||||
|
||||
// WithTx runs callbacks in a transaction as described in https://entgo.io/docs/transactions/#best-practices
|
||||
func WithTx(ctx context.Context, client *ent.Client, fn func(tx *ent.Tx) error) error {
|
||||
log := logger.FromContext(ctx)
|
||||
tx, err := client.Tx(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if v := recover(); v != nil {
|
||||
slog.ErrorContext(ctx, "panic", "v", v)
|
||||
log.ErrorContext(ctx, "panic", "v", v)
|
||||
_ = tx.Rollback()
|
||||
panic(v)
|
||||
}
|
||||
}()
|
||||
if err := fn(tx); err != nil {
|
||||
if rerr := tx.Rollback(); rerr != nil {
|
||||
err = fmt.Errorf("%w: rolling back transaction: %v", err, rerr)
|
||||
log.ErrorContext(ctx, "could not roll back tx", "err", rerr)
|
||||
err = fmt.Errorf("coult not roll back tx: %w: %w", rerr, err)
|
||||
} else {
|
||||
log.WarnContext(ctx, "rolled back failed tx", "err", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return fmt.Errorf("committing transaction: %w", err)
|
||||
log.ErrorContext(ctx, "could not commit transaction", "err", err)
|
||||
return fmt.Errorf("could not commit: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -26,10 +27,10 @@ type OrganizationHandler struct {
|
||||
db *ent.Client
|
||||
}
|
||||
|
||||
func (h *OrganizationHandler) GetCallerOrganizations(
|
||||
func (h *OrganizationHandler) ListCallerOrganizations(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.GetCallerOrganizationsRequest],
|
||||
) (*connect.Response[holos.GetCallerOrganizationsResponse], error) {
|
||||
req *connect.Request[holos.ListCallerOrganizationsRequest],
|
||||
) (*connect.Response[holos.ListCallerOrganizationsResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
@@ -55,7 +56,7 @@ func (h *OrganizationHandler) GetCallerOrganizations(
|
||||
rpcOrgs = append(rpcOrgs, OrganizationToRPC(dbOrg))
|
||||
}
|
||||
|
||||
res := connect.NewResponse(&holos.GetCallerOrganizationsResponse{
|
||||
res := connect.NewResponse(&holos.ListCallerOrganizationsResponse{
|
||||
User: UserToRPC(dbUser),
|
||||
Organizations: rpcOrgs,
|
||||
})
|
||||
@@ -65,13 +66,13 @@ func (h *OrganizationHandler) GetCallerOrganizations(
|
||||
func (h *OrganizationHandler) CreateCallerOrganization(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerOrganizationRequest],
|
||||
) (*connect.Response[holos.GetCallerOrganizationsResponse], error) {
|
||||
) (*connect.Response[holos.CreateCallerOrganizationResponse], error) {
|
||||
log := logger.FromContext(ctx)
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
// todo get user by iss, sub
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Email())
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Issuer(), authnID.Subject())
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
@@ -90,14 +91,14 @@ func (h *OrganizationHandler) CreateCallerOrganization(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbUser, err = dbUser.Update().
|
||||
AddOrganizations(org).
|
||||
Save(ctx)
|
||||
return err
|
||||
return tx.Organization.UpdateOne(org).AddUsers(dbUser).Exec(ctx)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, errors.Wrap(err))
|
||||
}
|
||||
log = log.With("organization", org)
|
||||
|
||||
log.InfoContext(ctx, "created organization")
|
||||
|
||||
// TODO: prefetch organizations
|
||||
dbOrgs, err := dbUser.QueryOrganizations().All(ctx)
|
||||
@@ -109,7 +110,7 @@ func (h *OrganizationHandler) CreateCallerOrganization(
|
||||
rpcOrgs = append(rpcOrgs, OrganizationToRPC(dbOrg))
|
||||
}
|
||||
|
||||
res := connect.NewResponse(&holos.GetCallerOrganizationsResponse{
|
||||
res := connect.NewResponse(&holos.CreateCallerOrganizationResponse{
|
||||
User: UserToRPC(dbUser),
|
||||
Organizations: rpcOrgs,
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
@@ -10,12 +9,11 @@ import (
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/ent/organization"
|
||||
"github.com/holos-run/holos/internal/ent/platform"
|
||||
entplatform "github.com/holos-run/holos/internal/ent/platform"
|
||||
"github.com/holos-run/holos/internal/ent/user"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
)
|
||||
|
||||
// NewPlatformHandler returns a new PlatformService implementation.
|
||||
@@ -28,22 +26,23 @@ type PlatformHandler struct {
|
||||
db *ent.Client
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) GetPlatforms(
|
||||
func (h *PlatformHandler) ListPlatforms(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.GetPlatformsRequest],
|
||||
) (*connect.Response[holos.GetPlatformsResponse], error) {
|
||||
req *connect.Request[holos.ListPlatformsRequest],
|
||||
) (*connect.Response[holos.ListPlatformsResponse], error) {
|
||||
_, reqDBOrg, err := getAuthnUsersOrg(ctx, req.Msg.OrgId, h.db)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
return getPlatformsResponse(reqDBOrg), nil
|
||||
resp := &holos.ListPlatformsResponse{Platforms: rpcPlatforms(reqDBOrg)}
|
||||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) AddPlatform(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.AddPlatformRequest],
|
||||
) (*connect.Response[holos.GetPlatformsResponse], error) {
|
||||
) (*connect.Response[holos.AddPlatformResponse], error) {
|
||||
dbUser, dbOrg, err := getAuthnUsersOrg(ctx, req.Msg.Platform.OrgId, h.db)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
@@ -54,19 +53,40 @@ func (h *PlatformHandler) AddPlatform(
|
||||
SetCreatorID(dbUser.ID).
|
||||
SetName(req.Msg.Platform.Name).
|
||||
SetDisplayName(req.Msg.Platform.DisplayName).
|
||||
SetConfigForm(req.Msg.Platform.RawConfig.Form).
|
||||
SetConfigValues(req.Msg.Platform.RawConfig.Values).
|
||||
SetConfigCue(req.Msg.Platform.RawConfig.Cue).
|
||||
SetConfigDefinition(req.Msg.Platform.RawConfig.Definition).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
resp := getPlatformsResponse(dbOrg)
|
||||
resp.Msg.Platforms = append(resp.Msg.Platforms, PlatformToRPC(platform))
|
||||
resp := &holos.AddPlatformResponse{Platforms: rpcPlatforms(dbOrg)}
|
||||
resp.Platforms = append(resp.Platforms, PlatformToRPC(platform))
|
||||
|
||||
return resp, nil
|
||||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) getPlatform(ctx context.Context, id string, uid authn.Identity) (*ent.Platform, error) {
|
||||
platformID, err := uuid.FromString(id)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.db.Platform.Query().
|
||||
Where(entplatform.ID(platformID)).
|
||||
Where(entplatform.HasOrganizationWith(
|
||||
organization.HasUsersWith(
|
||||
user.Iss(uid.Issuer()),
|
||||
user.Sub(uid.Subject()),
|
||||
))).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
} else {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) GetPlatform(ctx context.Context, req *connect.Request[holos.GetPlatformRequest]) (*connect.Response[holos.GetPlatformResponse], error) {
|
||||
@@ -75,84 +95,94 @@ func (h *PlatformHandler) GetPlatform(ctx context.Context, req *connect.Request[
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
platformID, err := uuid.FromString(req.Msg.PlatformId)
|
||||
p, err := h.getPlatform(ctx, req.Msg.PlatformId, authnID)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.db.Platform.Query().
|
||||
Where(platform.ID(platformID)).
|
||||
Where(platform.HasOrganizationWith(
|
||||
organization.HasUsersWith(
|
||||
user.Iss(authnID.Issuer()),
|
||||
user.Sub(authnID.Subject()),
|
||||
))).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
} else {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.GetPlatformResponse{Platform: PlatformToRPC(p)}), nil
|
||||
}
|
||||
|
||||
// GetForm provides the FormlyFieldConfig for the platform to make the web ui form for user input.
|
||||
func (h *PlatformHandler) GetForm(ctx context.Context, req *connect.Request[holos.GetPlatformFormRequest]) (*connect.Response[holos.PlatformForm], error) {
|
||||
// Boilerplate to get the platform by id where the user is a member of the org.
|
||||
func (h *PlatformHandler) PutModel(ctx context.Context, req *connect.Request[holos.PutModelRequest]) (*connect.Response[holos.PutModelResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
platformID, err := uuid.FromString(req.Msg.PlatformId)
|
||||
p, err := h.getPlatform(ctx, req.Msg.GetPlatformId(), authnID)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, errors.Wrap(err))
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
p, err := h.db.Platform.Query().
|
||||
Where(platform.ID(platformID)).
|
||||
Where(platform.HasOrganizationWith(
|
||||
organization.HasUsersWith(
|
||||
user.Iss(authnID.Issuer()),
|
||||
user.Sub(authnID.Subject()),
|
||||
))).
|
||||
Only(ctx)
|
||||
slog.WarnContext(ctx, "todo: validate the platform config against cue definitions", "action", "todo", "cue", len(p.Cue))
|
||||
|
||||
_, err = p.Update().
|
||||
SetModel(&holos.Model{Model: req.Msg.GetModel()}).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
} else {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
rpcPlatform := PlatformToRPC(p)
|
||||
res := connect.NewResponse(rpcPlatform.Config.Form)
|
||||
return res, nil
|
||||
return connect.NewResponse(&holos.PutModelResponse{Model: req.Msg.Model}), nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) GetModel(ctx context.Context, req *connect.Request[holos.GetModelRequest]) (*connect.Response[holos.GetModelResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.getPlatform(ctx, req.Msg.PlatformId, authnID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.GetModelResponse{Model: p.Model.Model}), nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) GetForm(ctx context.Context, req *connect.Request[holos.GetFormRequest]) (*connect.Response[holos.GetFormResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.getPlatform(ctx, req.Msg.GetPlatformId(), authnID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.GetFormResponse{Fields: p.Form.GetFields(), Model: p.Model.GetModel()}), nil
|
||||
}
|
||||
|
||||
func (h *PlatformHandler) PutForm(ctx context.Context, req *connect.Request[holos.PutFormRequest]) (*connect.Response[holos.PutFormResponse], error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
|
||||
p, err := h.getPlatform(ctx, req.Msg.GetPlatformId(), authnID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
_, err = p.Update().
|
||||
SetForm(&holos.Form{Fields: req.Msg.GetFields()}).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
resp := &holos.PutFormResponse{Fields: req.Msg.GetFields()}
|
||||
return connect.NewResponse(resp), nil
|
||||
}
|
||||
|
||||
func PlatformToRPC(platform *ent.Platform) *holos.Platform {
|
||||
var form holos.PlatformForm
|
||||
if err := json.Unmarshal(platform.ConfigForm, &form); err != nil {
|
||||
slog.Error("could not unmarshal platform config form", "platform_id", platform.ID.String(), "err", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &holos.Platform{
|
||||
Id: platform.ID.String(),
|
||||
Name: platform.Name,
|
||||
DisplayName: platform.DisplayName,
|
||||
OrgId: platform.OrgID.String(),
|
||||
Config: &holos.Config{Form: &form},
|
||||
Timestamps: &holos.Timestamps{
|
||||
CreatedAt: timestamppb.New(platform.CreatedAt),
|
||||
UpdatedAt: timestamppb.New(platform.UpdatedAt),
|
||||
},
|
||||
Creator: &holos.Creator{
|
||||
Id: platform.CreatorID.String(),
|
||||
},
|
||||
Spec: &holos.PlatformSpec{Model: platform.Model.GetModel()},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,14 +234,14 @@ func getAuthnUsersOrg(ctx context.Context, orgID string, db *ent.Client) (*ent.U
|
||||
return dbUser, reqDBOrg, nil
|
||||
}
|
||||
|
||||
func getPlatformsResponse(reqDBOrg *ent.Organization) *connect.Response[holos.GetPlatformsResponse] {
|
||||
// one extra in case a new platform is appended.
|
||||
rpcPlatforms := make([]*holos.Platform, 0, 1+len(reqDBOrg.Edges.Platforms))
|
||||
for _, platform := range reqDBOrg.Edges.Platforms {
|
||||
rpcPlatforms = append(rpcPlatforms, PlatformToRPC(platform))
|
||||
func rpcPlatforms(reqDBOrg *ent.Organization) []*holos.Platform {
|
||||
if reqDBOrg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.GetPlatformsResponse{
|
||||
Platforms: rpcPlatforms,
|
||||
})
|
||||
// one extra in case a new platform is appended.
|
||||
platforms := make([]*holos.Platform, 0, 1+len(reqDBOrg.Edges.Platforms))
|
||||
for _, platform := range reqDBOrg.Edges.Platforms {
|
||||
platforms = append(platforms, PlatformToRPC(platform))
|
||||
}
|
||||
return platforms
|
||||
}
|
||||
|
||||
900
internal/server/handler/system.go
Normal file
900
internal/server/handler/system.go
Normal file
@@ -0,0 +1,900 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/holos-run/holos/internal/ent"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/authn"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
holos "github.com/holos-run/holos/service/gen/holos/v1alpha1"
|
||||
)
|
||||
|
||||
const AdminEmail = "jeff@openinfrastructure.co"
|
||||
|
||||
// NewSystemHandler returns a new SystemService implementation.
|
||||
func NewSystemHandler(db *ent.Client) *SystemHandler {
|
||||
return &SystemHandler{db: db}
|
||||
}
|
||||
|
||||
// SystemHandler implements the PlatformService interface.
|
||||
type SystemHandler struct {
|
||||
db *ent.Client
|
||||
}
|
||||
|
||||
func (h *SystemHandler) checkAdmin(ctx context.Context) error {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
if authnID.Email() != AdminEmail {
|
||||
err := fmt.Errorf("not an admin:\n\thave (%+v)\n\twant (%+v)", authnID.Email(), AdminEmail)
|
||||
return connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *SystemHandler) DropTables(ctx context.Context, req *connect.Request[holos.DropTablesRequest]) (*connect.Response[holos.DropTablesResponse], error) {
|
||||
if err := h.checkAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
if err := WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
|
||||
var n int
|
||||
if n, err = tx.Platform.Delete().Exec(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.WarnContext(ctx, "deleted platforms", "count", n)
|
||||
if n, err = tx.Organization.Delete().Exec(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.WarnContext(ctx, "deleted organizations", "count", n)
|
||||
if n, err = tx.User.Delete().Exec(ctx); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.WarnContext(ctx, "deleted users", "count", n)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.DropTablesResponse{}), nil
|
||||
}
|
||||
|
||||
func (h *SystemHandler) SeedDatabase(ctx context.Context, req *connect.Request[holos.SeedDatabaseRequest]) (*connect.Response[holos.SeedDatabaseResponse], error) {
|
||||
if err := h.checkAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := WithTx(ctx, h.db, func(tx *ent.Tx) (err error) {
|
||||
jeff, err := tx.User.Create().
|
||||
SetID(uuid.FromStringOrNil("018f36fb-e3f2-7f7f-a72f-ce48eb16c82d")).
|
||||
SetEmail("jeff@openinfrastructure.co").
|
||||
SetIss("https://login.ois.run").
|
||||
SetSub("261773693724656988").
|
||||
SetName("Jeff McCune").
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
nate, err := tx.User.Create().
|
||||
SetEmail("nate@openinfrastructure.co").
|
||||
SetIss("https://login.ois.run").
|
||||
SetSub("261775487611699776").
|
||||
SetName("Nate McCurdy").
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
gary, err := tx.User.Create().
|
||||
SetEmail("gary@openinfrastructure.co").
|
||||
SetIss("https://login.ois.run").
|
||||
SetSub("261775531836441152").
|
||||
SetName("Gary Larizza").
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Create the org
|
||||
org, err := tx.Organization.Create().
|
||||
SetID(uuid.FromStringOrNil("018f36fb-e3f7-7f7f-a1c5-c85fb735d215")).
|
||||
SetName("ois").
|
||||
SetDisplayName("Open Infrastructure Services").
|
||||
SetCreator(jeff).
|
||||
Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Add org memebers
|
||||
org, err = org.Update().AddUsers(jeff, gary, nate).Save(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
var form holos.Form
|
||||
if err := json.Unmarshal([]byte(BareForm), &form); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
var model holos.Model
|
||||
if err := json.Unmarshal([]byte(Model), &model); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Add a platform
|
||||
err = tx.Platform.Create().
|
||||
SetID(uuid.FromStringOrNil("018f36fb-e3ff-7f7f-a5d1-7ca2bf499e94")).
|
||||
SetName("bare").
|
||||
SetDisplayName("Bare Platform").
|
||||
SetForm(&form).
|
||||
SetModel(&model).
|
||||
SetCreator(jeff).
|
||||
SetOrgID(org.ID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
stuff := []string{"Jeff", "Gary", "Nate"}
|
||||
for _, name := range stuff {
|
||||
err := tx.Platform.Create().
|
||||
SetName(strings.ToLower(name)).
|
||||
SetDisplayName(name + "'s Platform").
|
||||
SetForm(&form).
|
||||
SetModel(&model).
|
||||
SetCreator(jeff).
|
||||
SetOrgID(org.ID).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, connect.NewError(connect.CodeFailedPrecondition, errors.Wrap(err))
|
||||
}
|
||||
|
||||
return connect.NewResponse(&holos.SeedDatabaseResponse{}), nil
|
||||
}
|
||||
|
||||
const Model = `{"model":{}}`
|
||||
|
||||
const BareForm = `
|
||||
{
|
||||
"fields": [
|
||||
{
|
||||
"key": "org",
|
||||
"wrappers": [
|
||||
"holos-panel"
|
||||
],
|
||||
"props": {
|
||||
"label": "Organization",
|
||||
"description": "Organization config values are used to derive more specific configuration values throughout the platform."
|
||||
},
|
||||
"resetOnHide": true,
|
||||
"fieldGroup": [
|
||||
{
|
||||
"key": "name",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Name",
|
||||
"description": "DNS label, e.g. 'example'",
|
||||
"pattern": "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$",
|
||||
"minLength": 3,
|
||||
"maxLength": 30,
|
||||
"required": true
|
||||
},
|
||||
"validation": {
|
||||
"messages": {
|
||||
"pattern": "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
}
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "domain",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Domain",
|
||||
"placeholder": "example.com",
|
||||
"minLength": 3,
|
||||
"maxLength": 100,
|
||||
"description": "DNS domain, e.g. 'example.com'",
|
||||
"required": true
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "displayName",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Display Name",
|
||||
"placeholder": "Example Organization",
|
||||
"description": "Display name, e.g. 'Example Organization'",
|
||||
"maxLength": 100,
|
||||
"required": true
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "contactEmail",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Contact Email",
|
||||
"placeholder": "platform-team@example.com",
|
||||
"description": "Technical contact email address",
|
||||
"required": true
|
||||
},
|
||||
"resetOnHide": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "cloud",
|
||||
"wrappers": [
|
||||
"holos-panel"
|
||||
],
|
||||
"props": {
|
||||
"label": "Cloud Providers",
|
||||
"description": "Select the services that provide resources for the platform."
|
||||
},
|
||||
"resetOnHide": true,
|
||||
"fieldGroup": [
|
||||
{
|
||||
"key": "providers",
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "Select Providers",
|
||||
"description": "Select the cloud providers the platform builds upon.",
|
||||
"multiple": true,
|
||||
"selectAllOption": "Select All",
|
||||
"options": [
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
},
|
||||
"resetOnHide": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "aws",
|
||||
"wrappers": [
|
||||
"holos-panel"
|
||||
],
|
||||
"props": {
|
||||
"label": "Amazon Web Services",
|
||||
"description": "Provide the information necessary for Holos to manage AWS resources to provide the platform."
|
||||
},
|
||||
"expressions": {
|
||||
"hide": "!formState.model.cloud?.providers?.includes(\"aws\")"
|
||||
},
|
||||
"resetOnHide": true,
|
||||
"fieldGroup": [
|
||||
{
|
||||
"key": "primaryRoleARN",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Holos Admin Role ARN",
|
||||
"description": "Enter the AWS Role ARN Holos will use to bootstrap resources. For example, arn:aws:iam::123456789012:role/HolosAdminAccess",
|
||||
"pattern": "^arn:.*",
|
||||
"minLength": 4,
|
||||
"required": true
|
||||
},
|
||||
"validation": {
|
||||
"messages": {
|
||||
"pattern": "Must be a valid ARN. Refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html"
|
||||
}
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "regions",
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "Select Regions",
|
||||
"description": "Select the AWS regions this platform operates in.",
|
||||
"multiple": true,
|
||||
"required": true,
|
||||
"selectAllOption": "Select All",
|
||||
"options": [
|
||||
{
|
||||
"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": "cn-northwest-1",
|
||||
"label": "Ningxia (cn-northwest-1)"
|
||||
},
|
||||
{
|
||||
"value": "ap-southeast-3",
|
||||
"label": "Jakarta (ap-southeast-3)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resetOnHide": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "gcp",
|
||||
"wrappers": [
|
||||
"holos-panel"
|
||||
],
|
||||
"props": {
|
||||
"label": "Google Cloud Platform",
|
||||
"description": "Use this form to configure platform level GCP settings."
|
||||
},
|
||||
"expressions": {
|
||||
"hide": "!formState.model.cloud?.providers?.includes(\"gcp\")"
|
||||
},
|
||||
"resetOnHide": true,
|
||||
"fieldGroup": [
|
||||
{
|
||||
"key": "regions",
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "Select Regions",
|
||||
"description": "Select the GCP regions this platform operates in.",
|
||||
"multiple": true,
|
||||
"selectAllOption": "Select All",
|
||||
"options": [
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "gcpProjectID",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Project ID",
|
||||
"description": "Enter the project id where the provisioner cluster resides.",
|
||||
"pattern": "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$",
|
||||
"minLength": 6,
|
||||
"maxLength": 30,
|
||||
"required": true
|
||||
},
|
||||
"validation": {
|
||||
"messages": {
|
||||
"pattern": "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
}
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "gcpProjectNumber",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Project Number",
|
||||
"type": "number",
|
||||
"description": "Enter the project number where the provisioner cluster resides.",
|
||||
"pattern": "^[0-9]+$",
|
||||
"required": true
|
||||
},
|
||||
"validation": {
|
||||
"messages": {
|
||||
"pattern": "Must be a valid project number."
|
||||
}
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "provisionerCABundle",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Provisioner CA Bundle",
|
||||
"description": "Enter the provisioner cluster ca bundle. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.certificate-authority-data}'",
|
||||
"pattern": "^[0-9a-zA-Z]+=*$",
|
||||
"required": true
|
||||
},
|
||||
"validation": {
|
||||
"messages": {
|
||||
"pattern": "Must be a base64 encoded pem encoded certificate bundle."
|
||||
}
|
||||
},
|
||||
"resetOnHide": true
|
||||
},
|
||||
{
|
||||
"key": "provisionerURL",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Provisioner URL",
|
||||
"description": "Enter the URL of the provisioner cluster API endpoint. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.server}'",
|
||||
"pattern": "^https://.*$",
|
||||
"required": true
|
||||
},
|
||||
"validation": {
|
||||
"messages": {
|
||||
"pattern": "Must be a https:// URL."
|
||||
}
|
||||
},
|
||||
"resetOnHide": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "cloudflare",
|
||||
"wrappers": [
|
||||
"holos-panel"
|
||||
],
|
||||
"props": {
|
||||
"label": "Cloudflare",
|
||||
"description": "Cloudflare is primarily used for DNS automation."
|
||||
},
|
||||
"expressions": {
|
||||
"hide": "!formState.model.cloud?.providers?.includes(\"cloudflare\")"
|
||||
},
|
||||
"resetOnHide": true,
|
||||
"fieldGroup": [
|
||||
{
|
||||
"key": "email",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Account Email",
|
||||
"description": "Enter the Cloudflare email address to manage DNS",
|
||||
"minLength": 3,
|
||||
"required": true
|
||||
},
|
||||
"resetOnHide": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "github",
|
||||
"wrappers": [
|
||||
"holos-panel"
|
||||
],
|
||||
"props": {
|
||||
"label": "GitHub",
|
||||
"description": "GitHub is primarily used to host Git repositories and execute Actions workflows."
|
||||
},
|
||||
"expressions": {
|
||||
"hide": "!formState.model.cloud?.providers?.includes(\"github\")"
|
||||
},
|
||||
"resetOnHide": true,
|
||||
"fieldGroup": [
|
||||
{
|
||||
"key": "primaryOrg",
|
||||
"type": "input",
|
||||
"props": {
|
||||
"label": "Organization",
|
||||
"description": "Enter the primary GitHub organization associed with the platform.",
|
||||
"pattern": "^(?!-)(?!.*--)([a-zA-Z0-9]|-){1,39}$",
|
||||
"minLength": 1,
|
||||
"maxLength": 39,
|
||||
"required": true
|
||||
},
|
||||
"validation": {
|
||||
"messages": {
|
||||
"pattern": "All characters must be either a hyphen or alphanumeric. Cannot start with a hyphen. Cannot include consecutive hyphens."
|
||||
}
|
||||
},
|
||||
"resetOnHide": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "backups",
|
||||
"wrappers": [
|
||||
"holos-panel"
|
||||
],
|
||||
"props": {
|
||||
"label": "Backups",
|
||||
"description": "Configure platform level data backup settings. Requires AWS."
|
||||
},
|
||||
"resetOnHide": true,
|
||||
"fieldGroup": [
|
||||
{
|
||||
"key": "s3bucket",
|
||||
"type": "select",
|
||||
"props": {
|
||||
"label": "S3 Bucket Region",
|
||||
"description": "Select the S3 Bucket Region.",
|
||||
"multiple": true,
|
||||
"options": [
|
||||
{
|
||||
"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": "cn-northwest-1",
|
||||
"label": "Ningxia (cn-northwest-1)"
|
||||
},
|
||||
{
|
||||
"value": "ap-southeast-3",
|
||||
"label": "Jakarta (ap-southeast-3)"
|
||||
}
|
||||
]
|
||||
},
|
||||
"expressions": {
|
||||
"props.disabled": "!formState.model.cloud?.providers?.includes(\"aws\")",
|
||||
"props.required": "formState.model.cloud?.providers?.includes(\"aws\")",
|
||||
"props.description": "formState.model.cloud?.providers?.includes(\"aws\") ? 'Select the S3 Bucket Region.' : 'Enable AWS in the Cloud Provider section to configure backups.'"
|
||||
},
|
||||
"resetOnHide": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
@@ -56,7 +56,7 @@ func (h *UserHandler) GetCallerUser(
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
}
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Email())
|
||||
dbUser, err := getUser(ctx, h.db, authnID.Issuer(), authnID.Subject())
|
||||
if err != nil {
|
||||
if ent.MaskNotFound(err) == nil {
|
||||
return nil, connect.NewError(connect.CodeNotFound, errors.Wrap(err))
|
||||
@@ -68,10 +68,7 @@ func (h *UserHandler) GetCallerUser(
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) CreateCallerUser(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerUserRequest],
|
||||
) (*connect.Response[holos.CreateCallerUserResponse], error) {
|
||||
func (h *UserHandler) createCallerUser(ctx context.Context) (*ent.User, error) {
|
||||
authnID, err := authn.FromContext(ctx)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.Wrap(err))
|
||||
@@ -87,6 +84,18 @@ func (h *UserHandler) CreateCallerUser(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return createdUser, nil
|
||||
}
|
||||
|
||||
func (h *UserHandler) CreateCallerUser(
|
||||
ctx context.Context,
|
||||
req *connect.Request[holos.CreateCallerUserRequest],
|
||||
) (*connect.Response[holos.CreateCallerUserResponse], error) {
|
||||
createdUser, err := h.createCallerUser(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := connect.NewResponse(&holos.CreateCallerUserResponse{
|
||||
User: UserToRPC(createdUser),
|
||||
})
|
||||
@@ -107,11 +116,16 @@ func UserToRPC(u *ent.User) *holos.User {
|
||||
return &iamUser
|
||||
}
|
||||
|
||||
func getUser(ctx context.Context, client *ent.Client, email string) (*ent.User, error) {
|
||||
func getUser(ctx context.Context, client *ent.Client, iss string, sub string) (*ent.User, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
user, err := client.User.Query().Where(user.Email(email)).Only(ctx)
|
||||
user, err := client.User.Query().
|
||||
Where(
|
||||
user.Iss(iss),
|
||||
user.Sub(sub),
|
||||
).
|
||||
Only(ctx)
|
||||
if err != nil {
|
||||
log.DebugContext(ctx, "could not get user", "err", err, "email", email)
|
||||
log.DebugContext(ctx, "could not get user", "err", err, "iss", iss, "sub", sub)
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return user, nil
|
||||
|
||||
@@ -115,11 +115,13 @@ func (s *Server) registerConnectRpc() error {
|
||||
s.handle(holosconnect.NewUserServiceHandler(handler.NewUserHandler(s.db), opts))
|
||||
s.handle(holosconnect.NewOrganizationServiceHandler(handler.NewOrganizationHandler(s.db), opts))
|
||||
s.handle(holosconnect.NewPlatformServiceHandler(handler.NewPlatformHandler(s.db), opts))
|
||||
s.handle(holosconnect.NewSystemServiceHandler(handler.NewSystemHandler(s.db), opts))
|
||||
|
||||
reflector := grpcreflect.NewStaticReflector(
|
||||
holosconnect.UserServiceName,
|
||||
holosconnect.OrganizationServiceName,
|
||||
holosconnect.PlatformServiceName,
|
||||
holosconnect.SystemServiceName,
|
||||
)
|
||||
|
||||
s.mux.Handle(grpcreflect.NewHandlerV1(reflector))
|
||||
|
||||
@@ -20,16 +20,21 @@ message Organization {
|
||||
Creator creator = 5;
|
||||
}
|
||||
|
||||
message GetCallerOrganizationsRequest {}
|
||||
message ListCallerOrganizationsRequest {}
|
||||
|
||||
message GetCallerOrganizationsResponse {
|
||||
message ListCallerOrganizationsResponse {
|
||||
User user = 1;
|
||||
repeated Organization organizations = 2;
|
||||
}
|
||||
|
||||
message CreateCallerOrganizationRequest {}
|
||||
|
||||
service OrganizationService {
|
||||
rpc GetCallerOrganizations(GetCallerOrganizationsRequest) returns (GetCallerOrganizationsResponse) {}
|
||||
rpc CreateCallerOrganization(CreateCallerOrganizationRequest) returns (GetCallerOrganizationsResponse) {}
|
||||
message CreateCallerOrganizationResponse {
|
||||
User user = 1;
|
||||
repeated Organization organizations = 2;
|
||||
}
|
||||
|
||||
service OrganizationService {
|
||||
rpc ListCallerOrganizations(ListCallerOrganizationsRequest) returns (ListCallerOrganizationsResponse) {}
|
||||
rpc CreateCallerOrganization(CreateCallerOrganizationRequest) returns (CreateCallerOrganizationResponse) {}
|
||||
}
|
||||
|
||||
@@ -1,107 +1,111 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package holos.v1alpha1;
|
||||
|
||||
option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos";
|
||||
|
||||
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
|
||||
import "buf/validate/validate.proto";
|
||||
import "holos/v1alpha1/timestamps.proto";
|
||||
import "holos/v1alpha1/organization.proto";
|
||||
import "holos/v1alpha1/user.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
|
||||
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
|
||||
|
||||
// RawConfig represents the raw form configuration as opaque bytes. Used for input.
|
||||
message RawConfig {
|
||||
bytes form = 1;
|
||||
bytes values = 2;
|
||||
bytes cue = 3;
|
||||
string definition = 4;
|
||||
}
|
||||
|
||||
message Config {
|
||||
PlatformForm form = 1;
|
||||
}
|
||||
|
||||
message Platform {
|
||||
// Unique id assigned by the server.
|
||||
string id = 1;
|
||||
Timestamps timestamps = 2;
|
||||
|
||||
// Organization ID resource owner.
|
||||
string org_id = 3 [(buf.validate.field).string.uuid = true];
|
||||
string org_id = 2 [(buf.validate.field).string.uuid = true];
|
||||
// name is the platform short name as a dns label.
|
||||
string name = 4 [(buf.validate.field).string.max_len = 100];
|
||||
string display_name = 5 [(buf.validate.field).string.max_len = 100];
|
||||
Creator creator = 6;
|
||||
// config represents the platform config form and values. Read only.
|
||||
Config config = 7;
|
||||
// raw_config represents the platform config form and values. Write only.
|
||||
RawConfig raw_config = 8;
|
||||
string name = 3 [(buf.validate.field).string.max_len = 100];
|
||||
string display_name = 4 [(buf.validate.field).string.max_len = 100];
|
||||
PlatformSpec spec = 5;
|
||||
}
|
||||
|
||||
message FieldConfigProps {
|
||||
string label = 1;
|
||||
string placeholder = 2;
|
||||
string description = 3;
|
||||
bool required = 4;
|
||||
message PlatformSpec {
|
||||
// model represents the user-defined and user-supplied form field values.
|
||||
google.protobuf.Struct model = 1;
|
||||
}
|
||||
|
||||
message FieldConfig {
|
||||
string key = 1;
|
||||
string type = 2;
|
||||
FieldConfigProps props = 3;
|
||||
// Form represents the Formly input form.
|
||||
message Form {
|
||||
// fields represents FormlyFieldConfig[] encoded as a JSON array.
|
||||
repeated google.protobuf.Struct fields = 1;
|
||||
}
|
||||
|
||||
message ConfigSection {
|
||||
string name = 1;
|
||||
string displayName = 2;
|
||||
string description = 3;
|
||||
repeated FieldConfig fieldConfigs = 4;
|
||||
// Model represents the values entered into the form, stored in the form's model
|
||||
// in the web app, and persisted into the backend database. The model is
|
||||
// ultimately intended as the input to platform rendering.
|
||||
message Model {
|
||||
google.protobuf.Struct model = 1;
|
||||
}
|
||||
|
||||
message PlatformFormSpec {
|
||||
repeated ConfigSection sections = 1;
|
||||
}
|
||||
|
||||
message GetPlatformsRequest {
|
||||
message ListPlatformsRequest {
|
||||
string org_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
}
|
||||
|
||||
message GetPlatformsResponse {
|
||||
message ListPlatformsResponse {
|
||||
repeated Platform platforms = 1;
|
||||
}
|
||||
|
||||
message GetPlatformResponse {
|
||||
Platform platform = 1;
|
||||
}
|
||||
|
||||
message AddPlatformRequest {
|
||||
Platform platform = 1;
|
||||
}
|
||||
|
||||
message AddPlatformResponse {
|
||||
repeated Platform platforms = 1;
|
||||
}
|
||||
|
||||
message GetPlatformRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
}
|
||||
|
||||
message GetPlatformFormRequest {
|
||||
message GetPlatformResponse {
|
||||
Platform platform = 1;
|
||||
}
|
||||
|
||||
message GetFormRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
}
|
||||
|
||||
message MetadataName {
|
||||
string name = 1;
|
||||
message GetFormResponse {
|
||||
repeated google.protobuf.Struct fields = 1;
|
||||
google.protobuf.Struct model = 2;
|
||||
}
|
||||
|
||||
message PlatformForm {
|
||||
string apiVersion = 1;
|
||||
string kind = 2;
|
||||
MetadataName metadata = 3;
|
||||
PlatformFormSpec spec = 4;
|
||||
message GetModelRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
}
|
||||
|
||||
message GetModelResponse {
|
||||
google.protobuf.Struct model = 1;
|
||||
}
|
||||
|
||||
message PutModelRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
google.protobuf.Struct model = 2;
|
||||
}
|
||||
|
||||
message PutModelResponse {
|
||||
google.protobuf.Struct model = 1;
|
||||
}
|
||||
|
||||
message PutFormRequest {
|
||||
string platform_id = 1 [(buf.validate.field).string.uuid = true];
|
||||
repeated google.protobuf.Struct fields = 2;
|
||||
}
|
||||
|
||||
message PutFormResponse {
|
||||
repeated google.protobuf.Struct fields = 1;
|
||||
}
|
||||
|
||||
service PlatformService {
|
||||
rpc GetPlatforms(GetPlatformsRequest) returns (GetPlatformsResponse) {}
|
||||
rpc AddPlatform(AddPlatformRequest) returns (GetPlatformsResponse) {}
|
||||
rpc AddPlatform(AddPlatformRequest) returns (AddPlatformResponse) {}
|
||||
rpc GetPlatform(GetPlatformRequest) returns (GetPlatformResponse) {}
|
||||
rpc ListPlatforms(ListPlatformsRequest) returns (ListPlatformsResponse) {}
|
||||
|
||||
rpc GetForm(GetFormRequest) returns (GetFormResponse) {}
|
||||
rpc PutForm(PutFormRequest) returns (PutFormResponse) {}
|
||||
|
||||
rpc GetModel(GetModelRequest) returns (GetModelResponse) {}
|
||||
rpc PutModel(PutModelRequest) returns (PutModelResponse) {}
|
||||
}
|
||||
|
||||
20
service/holos/v1alpha1/system.proto
Normal file
20
service/holos/v1alpha1/system.proto
Normal file
@@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package holos.v1alpha1;
|
||||
|
||||
option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos";
|
||||
|
||||
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
|
||||
|
||||
// For validation, see the [Standard constraints](https://github.com/bufbuild/protovalidate/blob/main/docs/standard-constraints.md)
|
||||
|
||||
message SeedDatabaseRequest {}
|
||||
message SeedDatabaseResponse {}
|
||||
|
||||
message DropTablesRequest {}
|
||||
message DropTablesResponse {}
|
||||
|
||||
service SystemService {
|
||||
rpc SeedDatabase(SeedDatabaseRequest) returns (SeedDatabaseResponse) {}
|
||||
rpc DropTables(DropTablesRequest) returns (DropTablesResponse) {}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ package holos.v1alpha1;
|
||||
|
||||
option go_package = "github.com/holos-run/holos/service/gen/holos/v1alpha1;holos";
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
// git clone https://github.com/bufbuild/protovalidate then add <parent>/protovalidate/proto/protovalidate to your editor proto search path
|
||||
import "buf/validate/validate.proto";
|
||||
import "holos/v1alpha1/timestamps.proto";
|
||||
|
||||
@@ -1 +1 @@
|
||||
70
|
||||
74
|
||||
|
||||
@@ -1 +1 @@
|
||||
4
|
||||
0
|
||||
|
||||
Reference in New Issue
Block a user