mirror of
https://github.com/holos-run/holos.git
synced 2026-03-19 08:44:58 +00:00
Compare commits
105 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bc7804a9c | ||
|
|
a39807a858 | ||
|
|
5170650760 | ||
|
|
1d81b3c3b4 | ||
|
|
33970dafe8 | ||
|
|
faa46c54d8 | ||
|
|
42509a34cf | ||
|
|
ef369d4860 | ||
|
|
747ed3462a | ||
|
|
1fb1798f60 | ||
|
|
accf80200f | ||
|
|
4522ee1d4e | ||
|
|
313ebc6817 | ||
|
|
e0f439515f | ||
|
|
caa7560ab9 | ||
|
|
bbcf280da7 | ||
|
|
6d2daacb7b | ||
|
|
62f96a2d6c | ||
|
|
50f414d520 | ||
|
|
882f3894f3 | ||
|
|
30ddde7b49 | ||
|
|
5cced6fb51 | ||
|
|
a82ebf43b6 | ||
|
|
ebb6d6205a | ||
|
|
58950c469a | ||
|
|
0eebdaf0c7 | ||
|
|
54e2f28f4c | ||
|
|
d4d50ef12b | ||
|
|
075f2b16a4 | ||
|
|
6f8008a53c | ||
|
|
0618b52bae | ||
|
|
f1951c5db3 | ||
|
|
dad12acd8d | ||
|
|
a4503e076f | ||
|
|
09ddd339b8 | ||
|
|
bc94f4b6b8 | ||
|
|
564406f60f | ||
|
|
7845ce62e0 | ||
|
|
a1542752b7 | ||
|
|
7956475363 | ||
|
|
004ed56591 | ||
|
|
d497df3c27 | ||
|
|
3a8d46234f | ||
|
|
4d24dc5149 | ||
|
|
8eb7fbf7dc | ||
|
|
ffeeb7c553 | ||
|
|
c3c174155c | ||
|
|
2c2d2a9fd9 | ||
|
|
d692e2a6d5 | ||
|
|
e4cebddd0c | ||
|
|
0e48537d65 | ||
|
|
a461a96b9c | ||
|
|
9524c4f7c3 | ||
|
|
64b04d9cfd | ||
|
|
b419ad8caf | ||
|
|
8036c17916 | ||
|
|
220d498be0 | ||
|
|
0f5b6a2d6e | ||
|
|
36369d75c7 | ||
|
|
059b8283fd | ||
|
|
386eb2452a | ||
|
|
38e9a97fd2 | ||
|
|
ecca40e9d5 | ||
|
|
9d08e27e31 | ||
|
|
969bf5e867 | ||
|
|
3b5f28f4df | ||
|
|
df5619f988 | ||
|
|
a6d8383176 | ||
|
|
dbc7e374cd | ||
|
|
d81729857b | ||
|
|
d3d8a7b73c | ||
|
|
d9e6776b95 | ||
|
|
bde98faffa | ||
|
|
c2847554e0 | ||
|
|
9411a65dd8 | ||
|
|
9c1165e77e | ||
|
|
a02c7a4015 | ||
|
|
bdcde88e6f | ||
|
|
a32b100192 | ||
|
|
670d716403 | ||
|
|
bba3895f35 | ||
|
|
9e60ddbe85 | ||
|
|
44334fca52 | ||
|
|
2e2ed398c6 | ||
|
|
34f2a52cb7 | ||
|
|
d3888a884f | ||
|
|
3845871368 | ||
|
|
a3b2d19adb | ||
|
|
e4e7cd8c47 | ||
|
|
fb22e5521b | ||
|
|
d2ae766ae3 | ||
|
|
c0db949729 | ||
|
|
d2d4337ffd | ||
|
|
b0ca04635e | ||
|
|
198c66e6cd | ||
|
|
24346b9a38 | ||
|
|
0639562f1c | ||
|
|
c1fa9cc531 | ||
|
|
18653534ad | ||
|
|
2b89c33067 | ||
|
|
aee26d9375 | ||
|
|
7b04d492ab | ||
|
|
8abd03e165 | ||
|
|
2df843bc98 | ||
|
|
be4d2c29a5 |
8
Dockerfile
Normal file
8
Dockerfile
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM quay.io/holos-run/debian:bullseye AS final
|
||||
USER root
|
||||
WORKDIR /app
|
||||
ADD bin bin
|
||||
RUN chown -R app: /app
|
||||
# Kubernetes requires the user to be numeric
|
||||
USER 8192
|
||||
ENTRYPOINT bin/holos server
|
||||
24
Makefile
24
Makefile
@@ -7,7 +7,7 @@ REPO_PATH=$(ORG_PATH)/$(PROJ)
|
||||
VERSION := $(shell cat version/embedded/major version/embedded/minor version/embedded/patch | xargs printf "%s.%s.%s")
|
||||
BIN_NAME := holos
|
||||
|
||||
DOCKER_REPO=quay.io/openinfrastructure/holos
|
||||
DOCKER_REPO=quay.io/holos-run/holos
|
||||
IMAGE_NAME=$(DOCKER_REPO)
|
||||
|
||||
$( shell mkdir -p bin)
|
||||
@@ -16,10 +16,12 @@ $( shell mkdir -p bin)
|
||||
export PATH := $(PWD)/internal/frontend/holos/node_modules/.bin:$(PATH)
|
||||
|
||||
GIT_COMMIT=$(shell git rev-parse HEAD)
|
||||
GIT_SUFFIX=$(shell test -n "`git status --porcelain`" && echo "-dirty" || echo "")
|
||||
GIT_DETAIL=$(shell git describe --tags HEAD)
|
||||
GIT_TREE_STATE=$(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean")
|
||||
BUILD_DATE=$(shell date -Iseconds)
|
||||
|
||||
LD_FLAGS="-w -X ${ORG_PATH}/${PROJ}/version.GitCommit=${GIT_COMMIT} -X ${ORG_PATH}/${PROJ}/version.GitTreeState=${GIT_TREE_STATE} -X ${ORG_PATH}/${PROJ}/version.BuildDate=${BUILD_DATE}"
|
||||
LD_FLAGS="-w -X ${ORG_PATH}/${PROJ}/version.GitDescribe=${GIT_DETAIL}${GIT_SUFFIX} -X ${ORG_PATH}/${PROJ}/version.GitCommit=${GIT_COMMIT} -X ${ORG_PATH}/${PROJ}/version.GitTreeState=${GIT_TREE_STATE} -X ${ORG_PATH}/${PROJ}/version.BuildDate=${BUILD_DATE}"
|
||||
|
||||
.PHONY: default
|
||||
default: test
|
||||
@@ -53,6 +55,7 @@ tidy: ## Tidy go module.
|
||||
.PHONY: fmt
|
||||
fmt: ## Format code.
|
||||
cd docs/examples && cue fmt ./...
|
||||
cd internal/generate/platforms && cue fmt ./...
|
||||
go fmt ./...
|
||||
|
||||
.PHONY: vet
|
||||
@@ -61,7 +64,9 @@ vet: ## Vet Go code.
|
||||
|
||||
.PHONY: gencue
|
||||
gencue: ## Generate CUE definitions
|
||||
cd docs/examples && cue get go github.com/holos-run/holos/api/...
|
||||
cd internal/generate/platforms && cue get go github.com/holos-run/holos/api/v1alpha1/...
|
||||
cd internal/generate/platforms && cue get go github.com/holos-run/holos/api/core/...
|
||||
cd internal/generate/platforms && cue get go github.com/holos-run/holos/api/meta/...
|
||||
|
||||
.PHONY: rmgen
|
||||
rmgen: ## Remove generated code
|
||||
@@ -76,7 +81,7 @@ rmgen: ## Remove generated code
|
||||
regenerate: generate ## Re-generate code (delete and re-create)
|
||||
|
||||
.PHONY: generate
|
||||
generate: buf ## Generate code.
|
||||
generate: buf gencue ## Generate code.
|
||||
go generate ./...
|
||||
|
||||
.PHONY: build
|
||||
@@ -113,7 +118,7 @@ snapshot: ## Go release snapshot
|
||||
|
||||
.PHONY: buf
|
||||
buf: ## buf generate
|
||||
cd service && buf mod update
|
||||
cd service && buf dep update
|
||||
buf generate
|
||||
|
||||
.PHONY: tools
|
||||
@@ -121,11 +126,13 @@ tools: go-deps frontend-deps ## install tool dependencies
|
||||
|
||||
.PHONY: go-deps
|
||||
go-deps: ## tool versions pinned in tools.go
|
||||
go install cuelang.org/go/cmd/cue
|
||||
go install github.com/bufbuild/buf/cmd/buf
|
||||
go install github.com/fullstorydev/grpcurl/cmd/grpcurl
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go
|
||||
go install connectrpc.com/connect/cmd/protoc-gen-connect-go
|
||||
go install honnef.co/go/tools/cmd/staticcheck@latest
|
||||
go install honnef.co/go/tools/cmd/staticcheck
|
||||
go install golang.org/x/tools/cmd/godoc
|
||||
# curl https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash
|
||||
|
||||
.PHONY: frontend-deps
|
||||
@@ -145,6 +152,11 @@ frontend: buf
|
||||
cd internal/frontend/holos && ng build
|
||||
touch internal/frontend/frontend.go
|
||||
|
||||
.PHONY: image
|
||||
image: build ## Docker image build
|
||||
docker build . -t ${DOCKER_REPO}:v$(shell ./bin/holos --version)
|
||||
docker push ${DOCKER_REPO}:v$(shell ./bin/holos --version)
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display this help menu.
|
||||
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-20s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
|
||||
|
||||
38
api/core/v1alpha2/apiobjects.go
Normal file
38
api/core/v1alpha2/apiobjects.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package v1alpha2
|
||||
|
||||
import "google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
// Label is an arbitrary unique identifier. Defined as a type for clarity and type checking.
|
||||
type Label string
|
||||
|
||||
// Kind is a kubernetes api object kind. Defined as a type for clarity and type checking.
|
||||
type Kind string
|
||||
|
||||
// APIObjectMap represents the marshalled yaml representation of kubernetes api
|
||||
// objects. Do not produce an APIObjectMap directly, instead use [APIObjects]
|
||||
// to produce the marshalled yaml representation from CUE data, then provide the
|
||||
// result to [HolosComponent].
|
||||
type APIObjectMap map[Kind]map[Label]string
|
||||
|
||||
// APIObjects represents kubernetes api objects to apply to the api server.
|
||||
// Useful to mix in resources to each HolosComponent type, for example adding an
|
||||
// ExternalSecret to a HelmChart HolosComponent.
|
||||
//
|
||||
// Kind must be the resource kind, e.g. Deployment or Service.
|
||||
//
|
||||
// Label is an arbitrary internal identifier to uniquely identify the resource
|
||||
// within the context of a `holos` command. Holos will never write the
|
||||
// intermediate label to rendered output.
|
||||
//
|
||||
// Refer to [HolosComponent] which accepts an [APIObjectMap] field provided by
|
||||
// [APIObjects].
|
||||
type APIObjects struct {
|
||||
// APIObjects represents Kubernetes API objects defined directly from CUE
|
||||
// code. Useful to mix in resources, for example adding an ExternalSecret
|
||||
// resource to a HelmChart HolosComponent.
|
||||
APIObjects map[Kind]map[Label]structpb.Struct `json:"apiObjects"`
|
||||
// APIObjectMap represents the marshalled yaml representation of APIObjects,
|
||||
// useful to inspect the rendered representation of the resource which will be
|
||||
// sent to the kubernetes API server.
|
||||
APIObjectMap APIObjectMap `json:"apiObjectMap"`
|
||||
}
|
||||
116
api/core/v1alpha2/buildplan.go
Normal file
116
api/core/v1alpha2/buildplan.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package v1alpha2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FileContentMap represents a mapping of file names to file content.
|
||||
type FileContentMap map[string]string
|
||||
|
||||
// BuildPlan represents a build plan for the holos cli to execute. A build plan
|
||||
// is a set of zero or more holos components. The purpose of a BuildPlan is to
|
||||
// define one or more [HolosComponent] kinds, for example a [HelmChart] or
|
||||
// [KustomizeBuild].
|
||||
//
|
||||
// A BuildPlan usually has an additional empty [KubernetesObjects] for the
|
||||
// purpose of using the [HolosComponent] DeployFiles field to deploy an ArgoCD
|
||||
// or Flux gitops resource for the holos component.
|
||||
type BuildPlan struct {
|
||||
Kind string `json:"kind" cue:"\"BuildPlan\""`
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
Spec BuildPlanSpec `json:"spec"`
|
||||
}
|
||||
|
||||
func (bp *BuildPlan) Validate() error {
|
||||
errs := make([]string, 0, 2)
|
||||
if bp.Kind != BuildPlanKind {
|
||||
errs = append(errs, fmt.Sprintf("kind invalid: want: %s have: %s", BuildPlanKind, bp.Kind))
|
||||
}
|
||||
if bp.APIVersion != APIVersion {
|
||||
errs = append(errs, fmt.Sprintf("apiVersion invalid: want: %s have: %s", APIVersion, bp.APIVersion))
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("invalid BuildPlan: " + strings.Join(errs, ", "))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bp *BuildPlan) ResultCapacity() (count int) {
|
||||
if bp == nil {
|
||||
return 0
|
||||
}
|
||||
count = len(bp.Spec.Components.HelmChartList) +
|
||||
len(bp.Spec.Components.KubernetesObjectsList) +
|
||||
len(bp.Spec.Components.KustomizeBuildList) +
|
||||
len(bp.Spec.Components.Resources)
|
||||
return count
|
||||
}
|
||||
|
||||
type BuildPlanSpec struct {
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
Components BuildPlanComponents `json:"components,omitempty"`
|
||||
}
|
||||
|
||||
type BuildPlanComponents struct {
|
||||
Resources map[string]KubernetesObjects `json:"resources,omitempty"`
|
||||
KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty"`
|
||||
HelmChartList []HelmChart `json:"helmChartList,omitempty"`
|
||||
KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty"`
|
||||
}
|
||||
|
||||
// HolosComponent defines the fields common to all holos component kinds. Every
|
||||
// holos component kind should embed HolosComponent.
|
||||
type HolosComponent struct {
|
||||
// Kind is a string value representing the resource this object represents.
|
||||
Kind string `json:"kind"`
|
||||
// APIVersion represents the versioned schema of this representation of an object.
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
// Metadata represents data about the holos component such as the Name.
|
||||
Metadata Metadata `json:"metadata"`
|
||||
|
||||
// APIObjectMap holds the marshalled representation of api objects. Useful to
|
||||
// mix in resources to each HolosComponent type, for example adding an
|
||||
// ExternalSecret to a HelmChart HolosComponent. Refer to [APIObjects].
|
||||
APIObjectMap APIObjectMap `json:"apiObjectMap,omitempty"`
|
||||
|
||||
// DeployFiles represents file paths relative to the cluster deploy directory
|
||||
// with the value representing the file content. Intended for defining the
|
||||
// ArgoCD Application resource or Flux Kustomization resource from within CUE,
|
||||
// but may be used to render any file related to the build plan from CUE.
|
||||
DeployFiles FileContentMap `json:"deployFiles,omitempty"`
|
||||
|
||||
// Kustomize represents a kubectl kustomize build post-processing step.
|
||||
Kustomize `json:"kustomize,omitempty"`
|
||||
|
||||
// Skip causes holos to take no action regarding this component.
|
||||
Skip bool `json:"skip" cue:"bool | *false"`
|
||||
}
|
||||
|
||||
// Metadata represents data about the holos component such as the Name.
|
||||
type Metadata struct {
|
||||
// Name represents the name of the holos component.
|
||||
Name string `json:"name"`
|
||||
// Namespace is the primary namespace of the holos component. A holos
|
||||
// component may manage resources in multiple namespaces, in this case
|
||||
// consider setting the component namespace to default.
|
||||
//
|
||||
// This field is optional because not all resources require a namespace,
|
||||
// particularly CRD's and DeployFiles functionality.
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
// Kustomize represents resources necessary to execute a kustomize build.
|
||||
// Intended for at least two use cases:
|
||||
//
|
||||
// 1. Process a [KustomizeBuild] [HolosComponent] which represents raw yaml
|
||||
// file resources in a holos component directory.
|
||||
// 2. Post process a [HelmChart] [HolosComponent] to inject istio, patch jobs,
|
||||
// add custom labels, etc...
|
||||
type Kustomize struct {
|
||||
// KustomizeFiles holds file contents for kustomize, e.g. patch files.
|
||||
KustomizeFiles FileContentMap `json:"kustomizeFiles,omitempty"`
|
||||
// ResourcesFile is the file name used for api objects in kustomization.yaml
|
||||
ResourcesFile string `json:"resourcesFile,omitempty"`
|
||||
}
|
||||
11
api/core/v1alpha2/constants.go
Normal file
11
api/core/v1alpha2/constants.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package v1alpha2
|
||||
|
||||
const (
|
||||
APIVersion = "v1alpha2"
|
||||
BuildPlanKind = "BuildPlan"
|
||||
HelmChartKind = "HelmChart"
|
||||
// ChartDir is the directory name created in the holos component directory to cache a chart.
|
||||
ChartDir = "vendor"
|
||||
// ResourcesFile is the file name used to store component output when post-processing with kustomize.
|
||||
ResourcesFile = "resources.yaml"
|
||||
)
|
||||
49
api/core/v1alpha2/core.go
Normal file
49
api/core/v1alpha2/core.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Package v1alpha2 contains the core API contract between the holos cli and cue
|
||||
// configuration code. Platform designers, operators, and software developers
|
||||
// use this API to write configuration in CUE which `holos` loads. The overall
|
||||
// shape of the API defines imperative actions `holos` should carry out to
|
||||
// render the complete yaml that represents a Platform.
|
||||
package v1alpha2
|
||||
|
||||
import "google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
type PlatformMetadata struct {
|
||||
// Name represents the Platform name.
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// Platform represents a platform to manage. A Platform resource informs holos
|
||||
// which components to build. The platform resource also acts as a container
|
||||
// for the platform model form values provided by the PlatformService. The
|
||||
// primary use case is to collect the cluster names, cluster types, platform
|
||||
// model, and holos components to build into one resource.
|
||||
type Platform struct {
|
||||
// Kind is a string value representing the resource this object represents.
|
||||
Kind string `json:"kind" cue:"\"Platform\""`
|
||||
// APIVersion represents the versioned schema of this representation of an object.
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
// Metadata represents data about the object such as the Name.
|
||||
Metadata PlatformMetadata `json:"metadata"`
|
||||
|
||||
// Spec represents the specification.
|
||||
Spec PlatformSpec `json:"spec"`
|
||||
}
|
||||
|
||||
// PlatformSpec represents the specification of a Platform. Think of a platform
|
||||
// specification as a list of platform components to apply to a list of
|
||||
// kubernetes clusters combined with the user-specified Platform Model.
|
||||
type PlatformSpec struct {
|
||||
// Model represents the platform model holos gets from from the
|
||||
// PlatformService.GetPlatform rpc method and provides to CUE using a tag.
|
||||
Model structpb.Struct `json:"model"`
|
||||
// Components represents a list of holos components to manage.
|
||||
Components []PlatformSpecComponent `json:"components"`
|
||||
}
|
||||
|
||||
// PlatformSpecComponent represents a holos component to build or render.
|
||||
type PlatformSpecComponent struct {
|
||||
// Path is the path of the component relative to the platform root.
|
||||
Path string `json:"path"`
|
||||
// Cluster is the cluster name to provide when rendering the component.
|
||||
Cluster string `json:"cluster"`
|
||||
}
|
||||
38
api/core/v1alpha2/helm.go
Normal file
38
api/core/v1alpha2/helm.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package v1alpha2
|
||||
|
||||
// HelmChart represents a holos component which wraps around an upstream helm
|
||||
// chart. Holos orchestrates helm by providing values obtained from CUE,
|
||||
// renders the output using `helm template`, then post-processes the helm output
|
||||
// yaml using the general functionality provided by [HolosComponent], for
|
||||
// example [Kustomize] post-rendering and mixing in additional kubernetes api
|
||||
// objects.
|
||||
type HelmChart struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"HelmChart\""`
|
||||
|
||||
// Chart represents a helm chart to manage.
|
||||
Chart Chart `json:"chart"`
|
||||
// ValuesContent represents the values.yaml file holos passes to the `helm
|
||||
// template` command.
|
||||
ValuesContent string `json:"valuesContent"`
|
||||
// EnableHooks enables helm hooks when executing the `helm template` command.
|
||||
EnableHooks bool `json:"enableHooks" cue:"bool | *false"`
|
||||
}
|
||||
|
||||
// Chart represents a helm chart.
|
||||
type Chart struct {
|
||||
// Name represents the chart name.
|
||||
Name string `json:"name"`
|
||||
// Version represents the chart version.
|
||||
Version string `json:"version"`
|
||||
// Release represents the chart release when executing helm template.
|
||||
Release string `json:"release"`
|
||||
// Repository represents the repository to fetch the chart from.
|
||||
Repository Repository `json:"repository,omitempty"`
|
||||
}
|
||||
|
||||
// Repository represents a helm chart repository.
|
||||
type Repository struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
10
api/core/v1alpha2/kubernetesobjects.go
Normal file
10
api/core/v1alpha2/kubernetesobjects.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package v1alpha2
|
||||
|
||||
const KubernetesObjectsKind = "KubernetesObjects"
|
||||
|
||||
// KubernetesObjects represents a [HolosComponent] composed of kubernetes api
|
||||
// objects provided directly from CUE using [APIObjects].
|
||||
type KubernetesObjects struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"KubernetesObjects\""`
|
||||
}
|
||||
8
api/core/v1alpha2/kustomizebuild.go
Normal file
8
api/core/v1alpha2/kustomizebuild.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package v1alpha2
|
||||
|
||||
// KustomizeBuild renders plain yaml files in the holos component directory
|
||||
// using kubectl kustomize build.
|
||||
type KustomizeBuild struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"KustomizeBuild\""`
|
||||
}
|
||||
37
api/meta/v1alpha2/meta.go
Normal file
37
api/meta/v1alpha2/meta.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package v1alpha2
|
||||
|
||||
// TypeMeta describes an individual object in an API response or request with
|
||||
// strings representing the type of the object and its API schema version.
|
||||
// Structures that are versioned or persisted should inline TypeMeta.
|
||||
type TypeMeta struct {
|
||||
// Kind is a string value representing the resource this object represents.
|
||||
Kind string `json:"kind"`
|
||||
// APIVersion defines the versioned schema of this representation of an object.
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetKind() string {
|
||||
return tm.Kind
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetAPIVersion() string {
|
||||
return tm.APIVersion
|
||||
}
|
||||
|
||||
// Discriminator discriminates the kind of an api object.
|
||||
type Discriminator interface {
|
||||
// GetKind returns Kind.
|
||||
GetKind() string
|
||||
// GetAPIVersion returns APIVersion.
|
||||
GetAPIVersion() string
|
||||
}
|
||||
|
||||
// ObjectMeta represents metadata of a holos component object. The fields are a
|
||||
// copy of upstream kubernetes api machinery but are holos objects distinct from
|
||||
// kubernetes api objects.
|
||||
type ObjectMeta struct {
|
||||
// Name uniquely identifies the holos component instance and must be suitable as a file name.
|
||||
Name string `json:"name,omitempty"`
|
||||
// Namespace confines a holos component to a single namespace via kustomize if set.
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
@@ -9,14 +9,17 @@ import (
|
||||
type BuildPlan struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
// Metadata represents the holos component name
|
||||
Metadata ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
Spec BuildPlanSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
Platform map[string]any `json:"platform,omitempty" yaml:"platform,omitempty"`
|
||||
Metadata ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
Spec BuildPlanSpec `json:"spec,omitempty" yaml:"spec,omitempty"`
|
||||
}
|
||||
|
||||
type BuildPlanSpec struct {
|
||||
Disabled bool `json:"disabled,omitempty" yaml:"disabled,omitempty"`
|
||||
Components BuildPlanComponents `json:"components,omitempty" yaml:"components,omitempty"`
|
||||
// DeployFiles keys represent file paths relative to the cluster deploy
|
||||
// directory. Map values represent the string encoded file contents. Used to
|
||||
// write the argocd Application, but may be used to render any file from CUE.
|
||||
DeployFiles FileContentMap `json:"deployFiles,omitempty" yaml:"deployFiles,omitempty"`
|
||||
}
|
||||
|
||||
type BuildPlanComponents struct {
|
||||
|
||||
@@ -20,3 +20,11 @@ type HolosComponent struct {
|
||||
func (hc *HolosComponent) NewResult() *Result {
|
||||
return &Result{HolosComponent: *hc}
|
||||
}
|
||||
|
||||
func (hc *HolosComponent) GetAPIVersion() string {
|
||||
return hc.APIVersion
|
||||
}
|
||||
|
||||
func (hc *HolosComponent) GetKind() string {
|
||||
return hc.Kind
|
||||
}
|
||||
|
||||
13
api/v1alpha1/form.go
Normal file
13
api/v1alpha1/form.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package v1alpha1
|
||||
|
||||
import object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
|
||||
|
||||
// Form represents a collection of Formly json powered form.
|
||||
type Form struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
Spec FormSpec `json:"spec" yaml:"spec"`
|
||||
}
|
||||
|
||||
type FormSpec struct {
|
||||
Form object.Form `json:"form" yaml:"form"`
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
@@ -121,6 +122,14 @@ func (hc *HelmChart) helm(ctx context.Context, r *Result, path holos.InstancePat
|
||||
}
|
||||
|
||||
// cacheChart stores a cached copy of Chart in the chart subdirectory of path.
|
||||
//
|
||||
// It is assumed that the only method responsible for writing to chartDir is
|
||||
// cacheChart itself.
|
||||
//
|
||||
// This relies on the atomicity of moving temporary directories into place on
|
||||
// the same filesystem via os.Rename. If a syscall.EEXIST error occurs during
|
||||
// renaming, it indicates that the cached chart already exists, which is an
|
||||
// expected scenario when this function is called concurrently.
|
||||
func cacheChart(ctx context.Context, path holos.InstancePath, chartDir string, chart Chart) error {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
@@ -156,11 +165,16 @@ func cacheChart(ctx context.Context, path holos.InstancePath, chartDir string, c
|
||||
dst := filepath.Join(cachePath, item.Name())
|
||||
log.DebugContext(ctx, "rename", "src", src, "dst", dst)
|
||||
if err := os.Rename(src, dst); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not rename: %w", err))
|
||||
var linkErr *os.LinkError
|
||||
if errors.As(err, &linkErr) && errors.Is(linkErr.Err, syscall.EEXIST) {
|
||||
log.DebugContext(ctx, "cache already exists", "chart", chart.Name, "chart_version", chart.Version, "path", cachePath)
|
||||
} else {
|
||||
return errors.Wrap(fmt.Errorf("could not rename: %w", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "cached", "chart", chart.Name, "version", chart.Version, "path", cachePath)
|
||||
log.InfoContext(ctx, "cached", "chart", chart.Name, "chart_version", chart.Version, "path", cachePath)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
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.
|
||||
import "google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
// Platform represents a platform to manage. A Platform resource informs holos
|
||||
// which components to build. The platform resource also acts as a container
|
||||
// for the platform model form values provided by the PlatformService. The
|
||||
// primary use case is to collect the cluster names, cluster types, platform
|
||||
// model, and holos components to build into one resource.
|
||||
type Platform struct {
|
||||
TypeMeta `json:",inline" yaml:",inline"`
|
||||
Metadata ObjectMeta `json:"metadata,omitempty" yaml:"metadata,omitempty"`
|
||||
Metadata ObjectMeta `json:"metadata" yaml:"metadata"`
|
||||
Spec PlatformSpec `json:"spec" yaml:"spec"`
|
||||
}
|
||||
|
||||
// PlatformSpec represents the platform build plan specification.
|
||||
type PlatformSpec struct {
|
||||
// Model represents the platform model holos gets from from the
|
||||
// holos.platform.v1alpha1.PlatformService.GetPlatform method and provides to
|
||||
// CUE using a tag.
|
||||
Model structpb.Struct `json:"model" yaml:"model"`
|
||||
Components []PlatformSpecComponent `json:"components" yaml:"components"`
|
||||
}
|
||||
|
||||
// PlatformSpecComponent represents a component to build or render with flags to
|
||||
// pass, for example the cluster name.
|
||||
type PlatformSpecComponent struct {
|
||||
// Path is the path of the component relative to the platform root.
|
||||
Path string `json:"path" yaml:"path"`
|
||||
// Cluster is the cluster name to use when building the component.
|
||||
Cluster string `json:"cluster" yaml:"cluster"`
|
||||
}
|
||||
|
||||
@@ -17,6 +17,10 @@ type Result struct {
|
||||
HolosComponent
|
||||
// accumulatedOutput accumulates rendered api objects.
|
||||
accumulatedOutput string
|
||||
// DeployFiles keys represent file paths relative to the cluster deploy
|
||||
// directory. Map values represent the string encoded file contents. Used to
|
||||
// write the argocd Application, but may be used to render any file from CUE.
|
||||
DeployFiles FileContentMap `json:"deployFiles,omitempty" yaml:"deployFiles,omitempty"`
|
||||
}
|
||||
|
||||
// Continue returns true if Skip is true indicating the result is to be skipped over.
|
||||
@@ -40,11 +44,6 @@ func (r *Result) KustomizationFilename(writeTo string, cluster string) string {
|
||||
return filepath.Join(writeTo, "clusters", cluster, "holos", "components", r.Metadata.Name+"-kustomization.gen.yaml")
|
||||
}
|
||||
|
||||
// KustomizationContent returns the kustomization file contents to write.
|
||||
func (r *Result) KustomizationContent() string {
|
||||
return r.KsContent
|
||||
}
|
||||
|
||||
// AccumulatedOutput returns the accumulated rendered output.
|
||||
func (r *Result) AccumulatedOutput() string {
|
||||
return r.accumulatedOutput
|
||||
@@ -133,6 +132,21 @@ func (r *Result) kustomize(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Result) WriteDeployFiles(ctx context.Context, path string) error {
|
||||
log := logger.FromContext(ctx)
|
||||
if len(r.DeployFiles) == 0 {
|
||||
return nil
|
||||
}
|
||||
for k, content := range r.DeployFiles {
|
||||
path := filepath.Join(path, k)
|
||||
if err := r.Save(ctx, path, content); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.InfoContext(ctx, "wrote deploy file", "path", path, "bytes", len(content))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save writes the content to the filesystem for git ops.
|
||||
func (r *Result) Save(ctx context.Context, path string, content string) error {
|
||||
log := logger.FromContext(ctx)
|
||||
@@ -141,7 +155,7 @@ func (r *Result) Save(ctx context.Context, path string, content string) error {
|
||||
log.WarnContext(ctx, "could not mkdir", "path", dir, "err", err)
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Write the kube api objects
|
||||
// Write the file content
|
||||
if err := os.WriteFile(path, []byte(content), os.FileMode(0644)); err != nil {
|
||||
log.WarnContext(ctx, "could not write", "path", path, "err", err)
|
||||
return errors.Wrap(err)
|
||||
|
||||
@@ -10,7 +10,7 @@ func (tm *TypeMeta) GetKind() string {
|
||||
}
|
||||
|
||||
func (tm *TypeMeta) GetAPIVersion() string {
|
||||
return tm.Kind
|
||||
return tm.APIVersion
|
||||
}
|
||||
|
||||
// Discriminator is an interface to discriminate the kind api object.
|
||||
|
||||
3
cmd/holos/testdata/constraints.txt
vendored
3
cmd/holos/testdata/constraints.txt
vendored
@@ -2,6 +2,8 @@
|
||||
exec holos build ./foo/... --log-level debug
|
||||
stdout '^bf2bc7f9-9ba0-4f9e-9bd2-9a205627eb0b$'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- foo/constraints.cue --
|
||||
@@ -20,6 +22,7 @@ spec: components: KubernetesObjectsList: [
|
||||
package holos
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#KubernetesObjects: {
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
|
||||
3
cmd/holos/testdata/issue15_cue_errors.txt
vendored
3
cmd/holos/testdata/issue15_cue_errors.txt
vendored
@@ -3,12 +3,15 @@
|
||||
stderr 'apiObjectMap.foo.bar: cannot convert incomplete value'
|
||||
stderr '/component.cue:\d+:\d+$'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "BuildPlan"
|
||||
|
||||
@@ -3,6 +3,8 @@ exec holos build .
|
||||
stdout '^kind: SecretStore$'
|
||||
stdout '# Source: CUE apiObjects.SecretStore.default'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
@@ -13,6 +15,7 @@ kind: "BuildPlan"
|
||||
spec: components: KubernetesObjectsList: [{apiObjectMap: #APIObjects.apiObjectMap}]
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#SecretStore: {
|
||||
kind: string
|
||||
|
||||
@@ -4,6 +4,8 @@ stdout '^kind: SecretStore$'
|
||||
stdout '# Source: CUE apiObjects.SecretStore.default'
|
||||
stderr 'skipping helm: no chart name specified'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
@@ -14,6 +16,7 @@ kind: "BuildPlan"
|
||||
spec: components: HelmChartList: [{apiObjectMap: #APIObjects.apiObjectMap}]
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#SecretStore: {
|
||||
kind: string
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
! exec holos build .
|
||||
stderr 'apiObjects.secretstore.default.foo: field not allowed'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
@@ -10,6 +12,7 @@ package holos
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "KubernetesObjects"
|
||||
cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
#SecretStore: {
|
||||
metadata: name: string
|
||||
|
||||
3
cmd/holos/testdata/issue33_helm_stderr.txt
vendored
3
cmd/holos/testdata/issue33_helm_stderr.txt
vendored
@@ -2,6 +2,8 @@
|
||||
! exec holos build .
|
||||
stderr 'Error: execution error at \(zitadel/templates/secret_zitadel-masterkey.yaml:2:4\): Either set .Values.zitadel.masterkey xor .Values.zitadel.masterkeySecretName'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- zitadel.cue --
|
||||
@@ -12,6 +14,7 @@ kind: "BuildPlan"
|
||||
spec: components: HelmChartList: [_HelmChart]
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
_HelmChart: {
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# Kustomize is a supported holos component kind
|
||||
exec holos render --cluster-name=mycluster . --log-level=debug
|
||||
exec holos render component --cluster-name=mycluster . --log-level=debug
|
||||
|
||||
# Want generated output
|
||||
cmp want.yaml deploy/clusters/mycluster/components/kstest/kstest.gen.yaml
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "BuildPlan"
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
! exec holos build .
|
||||
stderr 'unknown field \\"TypoKubernetesObjectsList\\"'
|
||||
|
||||
-- platform.config.json --
|
||||
{}
|
||||
-- cue.mod --
|
||||
package holos
|
||||
-- component.cue --
|
||||
package holos
|
||||
_cluster: string @tag(cluster, string)
|
||||
_platform_config: string @tag(platform_config, string)
|
||||
|
||||
apiVersion: "holos.run/v1alpha1"
|
||||
kind: "BuildPlan"
|
||||
|
||||
2
cmd/holos/testdata/version.txt
vendored
2
cmd/holos/testdata/version.txt
vendored
@@ -1,5 +1,3 @@
|
||||
exec holos --version
|
||||
# want version with no v on stdout
|
||||
stdout -count=1 '^\d+\.\d+\.\d+$'
|
||||
# want nothing on stderr
|
||||
! stderr .
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
module: "github.com/holos-run/holos/docs/examples"
|
||||
language: version: "v0.9.2"
|
||||
|
||||
3
docs/examples/platform.config.cue
Normal file
3
docs/examples/platform.config.cue
Normal file
@@ -0,0 +1,3 @@
|
||||
package holos
|
||||
|
||||
_platform_config: string @tag(platform_config, type=string)
|
||||
1
docs/examples/platform.config.json
Normal file
1
docs/examples/platform.config.json
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -151,7 +151,7 @@ let OBJECTS = #APIObjects & {
|
||||
loopback: #Service & {
|
||||
_description: LoopbackDescription
|
||||
metadata: LoopbackMetaName
|
||||
spec: selector: LoopbackLabels
|
||||
spec: selector: LoopbackLabels
|
||||
spec: ports: [{port: 80, name: "http"}, {port: 443, name: "https"}]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ let OBJECTS = #APIObjects & {
|
||||
containers: [
|
||||
{
|
||||
name: Holos
|
||||
image: "271053619184.dkr.ecr.us-east-2.amazonaws.com/holos-run/holos-server/holos:v0.76.0"
|
||||
image: "271053619184.dkr.ecr.us-east-2.amazonaws.com/holos-run/holos-server/holos:v0.79.0"
|
||||
imagePullPolicy: "Always"
|
||||
env: [
|
||||
{
|
||||
|
||||
14
docs/runbooks/argocd.md
Normal file
14
docs/runbooks/argocd.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# ArgoCD
|
||||
|
||||
Create the deploy key secret in the management cluster.
|
||||
|
||||
```bash
|
||||
tmp="$(mktemp -d)"
|
||||
(cd $tmp && ssh-keygen -t ed25519 -f sshPrivateKey -m pem -C argocd -N '')
|
||||
echo git@github.com:holos-run/holos-infra.git > "${tmp}/url"
|
||||
holos create secret -n argocd --append-hash=false creds-holos-infra --from-file $tmp
|
||||
rm -rf "$tmp"
|
||||
```
|
||||
|
||||
When syncing the secret, the ExternalSecret needs to set the label
|
||||
`argocd.argoproj.io/secret-type: repo-creds`.
|
||||
97
docs/runbooks/login/backups.md
Normal file
97
docs/runbooks/login/backups.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# PostgresCluster Backups
|
||||
|
||||
This document describes how the S3 bucket for `PostgresCluster` backups is configured. These buckets are configured both for ZITADEL and for Holos
|
||||
Server and are applicable to any service in Holos that stores data in a pgo `PostgresCluster` resource.
|
||||
|
||||
## Create the Bucket
|
||||
Name: `holos-zitadel-backups` for `zitadel`
|
||||
Name: `holos-server-backups` for `holos server`
|
||||
> [!NOTE]
|
||||
> The settings below match the default settings recommended by AWS.
|
||||
|
||||
Object Ownership: `ACLs disabled` (recommended) Checked.
|
||||
Block Public Access settings for this bucket: **`Block all public access`** Checked.
|
||||
Bucket Versioning: `Disable`
|
||||
Default encryption: `Server-side encryption with Amazon S3 managed keys (SSE-S3)`
|
||||
Bucket Key: `Enable`
|
||||
Object Lock: `Disable`
|
||||
|
||||
## Create an IAM Policy
|
||||
Create one IAM Policy for each bucket to grant full access to the bucket. Replace the resource with each bucket name.
|
||||
Name: `holos-zitadel-backups` for `zitadel`
|
||||
Name: `holos-server-backups` for `holos server`
|
||||
Description: `Read and write access to a specific bucket for pgrest operating within a pgo PostgresCluster.`
|
||||
|
||||
Policy JSON:
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:GetBucketLocation",
|
||||
"s3:ListAllMyBuckets"
|
||||
],
|
||||
"Resource": "arn:aws:s3:::*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": "s3:*",
|
||||
"Resource": [
|
||||
"arn:aws:s3:::holos-zitadel-backups",
|
||||
"arn:aws:s3:::holos-zitadel-backups/*"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
## Create an IAM Group
|
||||
Create an IAM Group to attach the policy granting access to the bucket.
|
||||
Name: `holos-zitadel-backups` for `zitadel`
|
||||
Attach permission policies: `holos-zitadel-backups`
|
||||
|
||||
Name: `holos-server-backups` for `holos server`
|
||||
Attach permission policies: `holos-server-backups`
|
||||
## Create the IAM User
|
||||
Create an IAM User entity for each PostgresCluster. Do not provide user access to the AWS Management Console.
|
||||
Name: `holos-zitadel-backups` for `zitadel`
|
||||
Group: `holos-zitadel-backups`
|
||||
|
||||
Name: `holos-server-backups` for `holos server`
|
||||
Group: `holos-server-backups`
|
||||
|
||||
## Create an Access Key
|
||||
Create an access key for `pgbackrest` associated with the `PostgresCluster`.
|
||||
|
||||
Description:
|
||||
> Used by pgbackrest associated with the PostgresCluster resource. Refer to the PostgresCluster resource pgbackrest.cofiguration.secret.name for the stored location of the access key. Synced from the Management Cluster using an ExternalSecret.
|
||||
## Create the Secret
|
||||
Create a `Secret` in the holos management cluster usable by pgbackrest. This is a secret with a single key, `s3.conf` with the following format:
|
||||
```
|
||||
[global]
|
||||
repo2-cipher-pass=
|
||||
repo2-s3-key=
|
||||
repo2-s3-key-secret=
|
||||
repo3-cipher-pass=
|
||||
repo3-s3-key=
|
||||
repo3-s3-key-secret=
|
||||
```
|
||||
> [!NOTE]
|
||||
> Use the same values for repo2 and repo3. The purpose is to make space for migrating if need be in the future.
|
||||
|
||||
Generate the cipher pass using. This password is used to encrypt all backups using client side before the backup is written to the bucket.
|
||||
```
|
||||
tr -dc A-Za-z0-9 </dev/urandom | head -c 64
|
||||
```
|
||||
|
||||
Store the secret into the management cluster:
|
||||
```
|
||||
holos create secret --namespace zitadel holos-zitadel-backups \
|
||||
--append-hash=false --from-file .
|
||||
```
|
||||
|
||||
```
|
||||
holos create secret --namespace holos holos-server-backups \
|
||||
--append-hash=false --from-file .
|
||||
```
|
||||
30
docs/runbooks/namespace.md
Normal file
30
docs/runbooks/namespace.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Namespaces
|
||||
|
||||
Holos follows the [Namespace Sameness - Sig Multicluster Position][1]. A
|
||||
namespace is the same on all clusters within the scope of a platform.
|
||||
|
||||
Namespaces are also security boundaries for role based access control. As such,
|
||||
permission to read a secret in a namespace means the secret is readable on all
|
||||
clusters in the platform.
|
||||
|
||||
When adding a component to a platform, create a namespace using the following
|
||||
process. This ensures a namespace scoped `SecretStore` is created to sync
|
||||
`ExternalSecret` resources from the management cluster.
|
||||
|
||||
1. Add a new project to the `_Projects` struct in `platform.cue`.
|
||||
2. Add the namespace to the `spec.namespaces` field of the project.
|
||||
3. Render the platform
|
||||
4. Apply the `namespaces` component to the management cluster
|
||||
5. Apply the `eso-creds-manager` component to the management cluster to create the `eso-reader` ksa for the namespace `SecretStore`
|
||||
6. Get a timestamp: `STAMP="$(date +%s)"`
|
||||
7. Run the job to populate ecr creds: `kubectl create job -n holos-system --from=cronjob/ecr-creds-manager ecr-creds-manager-$STAMP`
|
||||
8. Wait for the job to complete: `kubectl -n holos-system logs -l job-name=ecr-creds-manager-$STAMP -f`
|
||||
9. Apply the `namespaces` component to the workload clusters
|
||||
10. On the workload cluster, run the job to fetch the eso-reader creds: `kubectl create job -n holos-system --from=cronjob/eso-creds-refresher eso-creds-refresher-${STAMP}`
|
||||
11. Wait for the job to complete: `kubectl -n holos-system logs -l job-name=eso-creds-refresher-${STAMP}`
|
||||
12. Apply the secretstores component to the workload cluster.
|
||||
13. Apply any other cluster specific components which were modified by the `holos render platform ./platform` command.
|
||||
|
||||
Your namespace is created and you have the ability to create secrets in the management cluster and pull them using ExternalSecret resources. (edited)
|
||||
|
||||
[1]: https://github.com/kubernetes/community/blob/dd4c8b704ef1c9c3bfd928c6fa9234276d61ad18/sig-multicluster/namespace-sameness-position-statement.md
|
||||
31
docs/runbooks/workload-identity.md
Normal file
31
docs/runbooks/workload-identity.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Workload Identity
|
||||
|
||||
When a new workload cluster is provisioned, allow it to access the Management
|
||||
Cluster using workload identity. This is necessary for the
|
||||
`eso-creds-refresher` component and `Job` that executes in each workload
|
||||
cluster, which in turn enables the `SecretStore` in each namespace to sync
|
||||
secrets.
|
||||
|
||||
Build the cluster with Cluster API.
|
||||
See https://github.com/holos-run/holos-infra/blob/main/hack/capi/eks/aws2/aws2-managedmachinepool.yaml#L81-L84
|
||||
|
||||
## Workload Identity Provider
|
||||
Add the Cluster as a workload identity provider to the `holos-ops` gcp project.
|
||||
|
||||
Pool: [holos](https://console.cloud.google.com/iam-admin/workload-identity-pools/pool/holos?organizationId=358674006047&project=holos-ops)
|
||||
Name: `k8s-aws1`, `k8s-aws2`, etc...
|
||||
### Issuer URL:
|
||||
```
|
||||
kubectl create -n default token default | cut -d. -f2 | base64 -d | jq -r .iss
|
||||
```
|
||||
|
||||
### Audience
|
||||
Use the default audience.
|
||||
### Attribute Mapping
|
||||
|
||||
| Google | OIDC |
|
||||
| -------------------------------- | ------------------------------------------------------ |
|
||||
| `google.subject` | `assertion.sub` |
|
||||
| `attribute.service_account_name` | `assertion['kubernetes.io']['serviceaccount']['name']` |
|
||||
| `attribute.uid` | `assertion['kubernetes.io']['serviceaccount']['uid']` |
|
||||
| `attribute.pod` | `assertion['kubernetes.io']['pod']['name']` |
|
||||
30
go.mod
30
go.mod
@@ -8,7 +8,7 @@ require (
|
||||
connectrpc.com/grpcreflect v1.2.0
|
||||
connectrpc.com/otelconnect v0.7.0
|
||||
connectrpc.com/validate v0.1.0
|
||||
cuelang.org/go v0.8.0
|
||||
cuelang.org/go v0.9.2
|
||||
entgo.io/ent v0.13.1
|
||||
github.com/bufbuild/buf v1.30.1
|
||||
github.com/choria-io/machine-room v0.0.0-20240417064836-c604da2f005e
|
||||
@@ -29,8 +29,9 @@ require (
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/tools v0.20.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/tools v0.22.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240325203815-454cdb8f5daa
|
||||
google.golang.org/protobuf v1.33.1-0.20240408130810-98873a205002
|
||||
honnef.co/go/tools v0.4.7
|
||||
@@ -44,9 +45,8 @@ require (
|
||||
|
||||
require (
|
||||
ariga.io/atlas v0.19.1-0.20240203083654-5948b60a8e43 // indirect
|
||||
cloud.google.com/go/compute v1.23.3 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e // indirect
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2 // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
@@ -86,7 +86,7 @@ require (
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/cli v26.0.0+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v26.0.0+incompatible // indirect
|
||||
github.com/docker/docker v26.0.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.1 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
@@ -215,6 +215,7 @@ require (
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
||||
github.com/tetratelabs/wazero v1.6.0 // indirect
|
||||
github.com/tidwall/gjson v1.17.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
@@ -225,6 +226,7 @@ require (
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xlab/tablewriter v0.0.0-20160610135559-80b567a11ad5 // indirect
|
||||
github.com/yashtewari/glob-intersection v0.2.0 // indirect
|
||||
github.com/yuin/goldmark v1.4.13 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zclconf/go-cty v1.8.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
@@ -236,17 +238,15 @@ require (
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20221208152030-732eee02a75a // indirect
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/mod v0.18.0 // indirect
|
||||
golang.org/x/oauth2 v0.20.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/term v0.21.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240325203815-454cdb8f5daa // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
|
||||
55
go.sum
55
go.sum
@@ -23,10 +23,8 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk=
|
||||
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
@@ -46,10 +44,10 @@ connectrpc.com/otelconnect v0.7.0 h1:ZH55ZZtcJOTKWWLy3qmL4Pam4RzRWBJFOqTPyAqCXkY
|
||||
connectrpc.com/otelconnect v0.7.0/go.mod h1:Bt2ivBymHZHqxvo4HkJ0EwHuUzQN6k2l0oH+mp/8nwc=
|
||||
connectrpc.com/validate v0.1.0 h1:r55jirxMK7HO/xZwVHj3w2XkVFarsUM77ZDy367NtH4=
|
||||
connectrpc.com/validate v0.1.0/go.mod h1:GU47c9/x/gd+u9wRSPkrQOP46gx2rMN+Wo37EHgI3Ow=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e h1:GwCVItFUPxwdsEYnlUcJ6PJxOjTeFFCKOh6QWg4oAzQ=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240314152124-224736b49f2e/go.mod h1:ApHceQLLwcOkCEXM1+DyCXTHEJhNGDpJ2kmV6axsx24=
|
||||
cuelang.org/go v0.8.0 h1:fO1XPe/SUGtc7dhnGnTPbpIDoQm/XxhDtoSF7jzO01c=
|
||||
cuelang.org/go v0.8.0/go.mod h1:CoDbYolfMms4BhWUlhD+t5ORnihR7wvjcfgyO9lL5FI=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2 h1:BnG6pr9TTr6CYlrJznYUDj6V7xldD1W+1iXPum0wT/w=
|
||||
cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2/go.mod h1:pK23AUVXuNzzTpfMCA06sxZGeVQ/75FdVtW249de9Uo=
|
||||
cuelang.org/go v0.9.2 h1:pfNiry2PdRBr02G/aKm5k2vhzmqbAOoaB4WurmEbWvs=
|
||||
cuelang.org/go v0.9.2/go.mod h1:qpAYsLOf7gTM1YdEg6cxh553uZ4q9ZDWlPbtZr9q1Wk=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
entgo.io/ent v0.13.1 h1:uD8QwN1h6SNphdCCzmkMN3feSUzNnVvV/WIkHKMbzOE=
|
||||
entgo.io/ent v0.13.1/go.mod h1:qCEmo+biw3ccBn9OyL4ZK5dfpwg++l1Gxwac5B1206A=
|
||||
@@ -187,8 +185,8 @@ github.com/docker/cli v26.0.0+incompatible h1:90BKrx1a1HKYpSnnBFR6AgDq/FqkHxwlUy
|
||||
github.com/docker/cli v26.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v26.0.0+incompatible h1:Ng2qi+gdKADUa/VM+6b6YaY2nlZhk/lVJiKR/2bMudU=
|
||||
github.com/docker/docker v26.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE=
|
||||
github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo=
|
||||
github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
@@ -638,6 +636,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
|
||||
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g=
|
||||
github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
|
||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
@@ -670,6 +670,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
@@ -719,8 +720,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -758,8 +759,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -793,16 +794,16 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
|
||||
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -868,16 +869,16 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -885,12 +886,12 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -942,8 +943,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
|
||||
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -970,8 +971,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
||||
8
holos.go
8
holos.go
@@ -1,10 +1,10 @@
|
||||
// Package holos defines types for the rest of the system.
|
||||
package holos
|
||||
|
||||
// A PathCueMod is a string representing the filesystem path of a cue module.
|
||||
// It is given a unique type so the API is clear.
|
||||
// A PathCueMod is a string representing the absolute filesystem path of a cue
|
||||
// module. It is given a unique type so the API is clear.
|
||||
type PathCueMod string
|
||||
|
||||
// A InstancePath is a string representing the filesystem path of a holos instance.
|
||||
// It is given a unique type so the API is clear.
|
||||
// A InstancePath is a string representing the absolute filesystem path of a
|
||||
// holos instance. It is given a unique type so the API is clear.
|
||||
type InstancePath string
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// Package v1alpha1 is the unstable version of the core api.
|
||||
package v1alpha1
|
||||
@@ -10,25 +10,30 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"cuelang.org/go/cue/build"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/load"
|
||||
"github.com/holos-run/holos/api/core/v1alpha2"
|
||||
"github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
"github.com/holos-run/holos"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/render"
|
||||
)
|
||||
|
||||
const (
|
||||
KubernetesObjects = v1alpha1.KubernetesObjectsKind
|
||||
KubernetesObjects = v1alpha2.KubernetesObjectsKind
|
||||
// Helm is the value of the kind field of holos build output indicating helm
|
||||
// values and helm command information.
|
||||
Helm = v1alpha1.HelmChartKind
|
||||
Helm = v1alpha2.HelmChartKind
|
||||
// Skip is the value when the instance should be skipped
|
||||
Skip = "Skip"
|
||||
// KustomizeBuild is the value of the kind field of cue output indicating holos should process the component using kustomize build to render output.
|
||||
// KustomizeBuild is the value of the kind field of cue output indicating
|
||||
// holos should process the component using kustomize build to render output.
|
||||
KustomizeBuild = v1alpha1.KustomizeBuildKind
|
||||
)
|
||||
|
||||
@@ -70,7 +75,7 @@ func (b *Builder) Cluster() string {
|
||||
}
|
||||
|
||||
// Instances returns the cue build instances being built.
|
||||
func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
|
||||
func (b *Builder) Instances(ctx context.Context, cfg *client.Config) ([]*build.Instance, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
mod, err := b.findCueMod()
|
||||
@@ -79,7 +84,29 @@ func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
|
||||
}
|
||||
dir := string(mod)
|
||||
|
||||
cfg := load.Config{Dir: dir}
|
||||
cueConfig := load.Config{Dir: dir}
|
||||
|
||||
// Get the platform model from the PlatformConfig
|
||||
pc, err := client.LoadPlatformConfig(ctx, dir)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
data, err := json.Marshal(pc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Refer to https://github.com/cue-lang/cue/blob/v0.7.0/cmd/cue/cmd/common.go#L429
|
||||
cueConfig.Tags = append(cueConfig.Tags, "platform_config="+string(data))
|
||||
if b.Cluster() != "" {
|
||||
cueConfig.Tags = append(cueConfig.Tags, "cluster="+b.Cluster())
|
||||
}
|
||||
log.DebugContext(ctx, fmt.Sprintf("cue: tags %v", cueConfig.Tags))
|
||||
|
||||
prefix := []string{"cue", "export", "--out", "yaml"}
|
||||
for _, tag := range cueConfig.Tags {
|
||||
prefix = append(prefix, "-t", fmt.Sprintf("'%s'", tag))
|
||||
}
|
||||
|
||||
// Make args relative to the module directory
|
||||
args := make([]string, len(b.cfg.args))
|
||||
@@ -94,25 +121,24 @@ func (b *Builder) Instances(ctx context.Context) ([]*build.Instance, error) {
|
||||
}
|
||||
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)
|
||||
|
||||
equiv := make([]string, len(prefix), 1+len(prefix))
|
||||
copy(equiv, prefix)
|
||||
equiv = append(equiv, relPath)
|
||||
log.Debug(strings.Join(equiv, " "), "comment", "cue equivalent command")
|
||||
}
|
||||
|
||||
// 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
|
||||
return load.Instances(args, &cueConfig), nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context) (results []*v1alpha1.Result, err error) {
|
||||
func (b *Builder) Run(ctx context.Context, cfg *client.Config) (results []*render.Result, err error) {
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "cue: building instances")
|
||||
instances, err := b.Instances(ctx)
|
||||
instances, err := b.Instances(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = make([]*v1alpha1.Result, 0, len(instances)*8)
|
||||
results = make([]*render.Result, 0, len(instances)*8)
|
||||
|
||||
// Each CUE instance provides a BuildPlan
|
||||
for idx, instance := range instances {
|
||||
@@ -127,7 +153,7 @@ func (b *Builder) Run(ctx context.Context) (results []*v1alpha1.Result, err erro
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (results []*v1alpha1.Result, err error) {
|
||||
func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (results []*render.Result, err error) {
|
||||
path := holos.InstancePath(instance.Dir)
|
||||
log := logger.FromContext(ctx).With("dir", path)
|
||||
|
||||
@@ -161,37 +187,31 @@ func (b Builder) runInstance(ctx context.Context, instance *build.Instance) (res
|
||||
|
||||
// New decoder for the full object
|
||||
decoder = json.NewDecoder(bytes.NewReader(jsonBytes))
|
||||
|
||||
// TODO: When we release v1, explicitly allow unknown fields so we can add
|
||||
// fields without needing to bump the major version. Disallow until we reach
|
||||
// v1 for clear error reporting.
|
||||
decoder.DisallowUnknownFields()
|
||||
|
||||
switch tm.Kind {
|
||||
case "BuildPlan":
|
||||
var bp v1alpha1.BuildPlan
|
||||
var bp v1alpha2.BuildPlan
|
||||
if err = decoder.Decode(&bp); err != nil {
|
||||
err = errors.Wrap(fmt.Errorf("could not decode BuildPlan %s: %w", instance.Dir, err))
|
||||
return
|
||||
}
|
||||
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
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
results, err = b.buildPlatform(ctx, &pf)
|
||||
default:
|
||||
err = errors.Wrap(fmt.Errorf("unknown kind: %v", tm.Kind))
|
||||
}
|
||||
|
||||
return
|
||||
return results, err
|
||||
}
|
||||
|
||||
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) {
|
||||
func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha2.BuildPlan, path holos.InstancePath) (results []*render.Result, err error) {
|
||||
log := logger.FromContext(ctx)
|
||||
|
||||
if err := buildPlan.Validate(); err != nil {
|
||||
@@ -204,11 +224,12 @@ func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha1.BuildPlan,
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: concurrent renders
|
||||
results = make([]*v1alpha1.Result, 0, buildPlan.ResultCapacity())
|
||||
results = make([]*render.Result, 0, buildPlan.ResultCapacity())
|
||||
log.DebugContext(ctx, "allocated results slice", "cap", buildPlan.ResultCapacity())
|
||||
|
||||
for _, component := range buildPlan.Spec.Components.Resources {
|
||||
if result, err := component.Render(ctx, path); err != nil {
|
||||
ko := render.KubernetesObjects{Component: component}
|
||||
if result, err := ko.Render(ctx, path); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
@@ -216,21 +237,24 @@ func (b *Builder) buildPlan(ctx context.Context, buildPlan *v1alpha1.BuildPlan,
|
||||
}
|
||||
|
||||
for _, component := range buildPlan.Spec.Components.KubernetesObjectsList {
|
||||
if result, err := component.Render(ctx, path); err != nil {
|
||||
ko := render.KubernetesObjects{Component: component}
|
||||
if result, err := ko.Render(ctx, path); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
}
|
||||
}
|
||||
for _, component := range buildPlan.Spec.Components.HelmChartList {
|
||||
if result, err := component.Render(ctx, path); err != nil {
|
||||
hc := render.HelmChart{Component: component}
|
||||
if result, err := hc.Render(ctx, path); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
}
|
||||
}
|
||||
for _, component := range buildPlan.Spec.Components.KustomizeBuildList {
|
||||
if result, err := component.Render(ctx, path); err != nil {
|
||||
kb := render.KustomizeBuild{Component: component}
|
||||
if result, err := kb.Render(ctx, path); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not render: %w", err))
|
||||
} else {
|
||||
results = append(results, result)
|
||||
|
||||
92
internal/builder/platform.go
Normal file
92
internal/builder/platform.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"cuelang.org/go/cue/build"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"github.com/holos-run/holos"
|
||||
core "github.com/holos-run/holos/api/core/v1alpha2"
|
||||
meta "github.com/holos-run/holos/api/meta/v1alpha2"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
)
|
||||
|
||||
// Platform builds a platform
|
||||
func (b *Builder) Platform(ctx context.Context, cfg *client.Config) (*core.Platform, error) {
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "cue: building platform instance")
|
||||
instances, err := b.Instances(ctx, cfg)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
|
||||
if len(instances) != 1 {
|
||||
return nil, errors.Wrap(errors.New(fmt.Sprintf("instances length %d must be exactly 1", len(instances))))
|
||||
}
|
||||
|
||||
// We only process the first instance, assume the render platform subcommand enforces this.
|
||||
instance := instances[0]
|
||||
log.DebugContext(ctx, "cue: building instance", "dir", instance.Dir)
|
||||
p, err := b.runPlatform(ctx, instance)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not build platform: %w", err))
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (b Builder) runPlatform(ctx context.Context, instance *build.Instance) (*core.Platform, error) {
|
||||
path := holos.InstancePath(instance.Dir)
|
||||
log := logger.FromContext(ctx).With("dir", path)
|
||||
|
||||
if err := instance.Err; err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not load: %w", err))
|
||||
}
|
||||
cueCtx := cuecontext.New()
|
||||
value := cueCtx.BuildInstance(instance)
|
||||
if err := value.Err(); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not build %s: %w", instance.Dir, err))
|
||||
}
|
||||
log.DebugContext(ctx, "cue: validating instance")
|
||||
if err := value.Validate(); err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not validate: %w", err))
|
||||
}
|
||||
|
||||
log.DebugContext(ctx, "cue: decoding holos platform")
|
||||
jsonBytes, err := value.MarshalJSON()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("could not marshal cue instance %s: %w", instance.Dir, err))
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
|
||||
// Discriminate the type of build plan.
|
||||
tm := &meta.TypeMeta{}
|
||||
err = decoder.Decode(tm)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(fmt.Errorf("invalid platform: %s: %w", instance.Dir, err))
|
||||
}
|
||||
|
||||
log.DebugContext(ctx, "cue: discriminated build kind: "+tm.GetKind(), "kind", tm.GetKind(), "apiVersion", tm.GetAPIVersion())
|
||||
|
||||
// New decoder for the full object
|
||||
decoder = json.NewDecoder(bytes.NewReader(jsonBytes))
|
||||
decoder.DisallowUnknownFields()
|
||||
|
||||
var pf core.Platform
|
||||
switch tm.GetKind() {
|
||||
case "Platform":
|
||||
if err = decoder.Decode(&pf); err != nil {
|
||||
err = errors.Wrap(fmt.Errorf("could not decode platform %s: %w", instance.Dir, err))
|
||||
return nil, err
|
||||
}
|
||||
return &pf, nil
|
||||
default:
|
||||
err = errors.Wrap(fmt.Errorf("unknown kind: %v", tm.GetKind()))
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
@@ -7,22 +7,26 @@ import (
|
||||
|
||||
"github.com/holos-run/holos/internal/builder"
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// makeBuildRunFunc returns the internal implementation of the build cli command
|
||||
func makeBuildRunFunc(cfg *holos.Config) command.RunFunc {
|
||||
func makeBuildRunFunc(cfg *client.Config) command.RunFunc {
|
||||
return func(cmd *cobra.Command, args []string) error {
|
||||
build := builder.New(builder.Entrypoints(args), builder.Cluster(cfg.ClusterName()))
|
||||
results, err := build.Run(cmd.Context())
|
||||
ctx := cmd.Root().Context()
|
||||
logger.FromContext(ctx).DebugContext(ctx, "RunE", "args", args)
|
||||
build := builder.New(builder.Entrypoints(args), builder.Cluster(cfg.Holos().ClusterName()))
|
||||
results, err := build.Run(ctx, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
outs := make([]string, 0, len(results))
|
||||
for idx, result := range results {
|
||||
if result == nil || result.Skip {
|
||||
if result.Continue() {
|
||||
slog.Debug("skip result", "idx", idx, "result", result)
|
||||
continue
|
||||
}
|
||||
@@ -42,7 +46,12 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("build [directory...]")
|
||||
cmd.Args = cobra.MinimumNArgs(1)
|
||||
cmd.Short = "build kubernetes api objects from a directory"
|
||||
cmd.RunE = makeBuildRunFunc(cfg)
|
||||
|
||||
cmd.Flags().AddGoFlagSet(cfg.ClusterFlagSet())
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
cmd.RunE = makeBuildRunFunc(config)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/version"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -20,9 +17,6 @@ func New(name string) *cobra.Command {
|
||||
CompletionOptions: cobra.CompletionOptions{
|
||||
HiddenDefaultCmd: true,
|
||||
},
|
||||
RunE: func(c *cobra.Command, args []string) error {
|
||||
return errors.Wrap(fmt.Errorf("could not run %v: not implemented", c.Name()))
|
||||
},
|
||||
SilenceUsage: true,
|
||||
SilenceErrors: true,
|
||||
}
|
||||
|
||||
@@ -3,21 +3,52 @@ package create
|
||||
import (
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/cli/secret"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// New returns the create command for the cli
|
||||
func New(hc *holos.Config) *cobra.Command {
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("create")
|
||||
cmd.Short = "create resources"
|
||||
cmd.Flags().SortFlags = false
|
||||
cmd.RunE = func(c *cobra.Command, args []string) error {
|
||||
return c.Usage()
|
||||
}
|
||||
|
||||
// api client config
|
||||
config := client.NewConfig(cfg)
|
||||
|
||||
// flags
|
||||
cmd.PersistentFlags().SortFlags = false
|
||||
// commands
|
||||
cmd.AddCommand(secret.NewCreateCmd(hc))
|
||||
cmd.AddCommand(secret.NewCreateCmd(cfg))
|
||||
cmd.AddCommand(NewPlatform(config))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatform(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("platform")
|
||||
|
||||
cmd.Short = "create a platform"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
pm := client.PlatformMutation{}
|
||||
cmd.Flags().AddGoFlagSet(pm.FlagSet())
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
client := client.New(cfg)
|
||||
pf, err := client.CreatePlatform(ctx, pm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log := logger.FromContext(ctx)
|
||||
log.InfoContext(ctx, "created platform", "name", pf.GetName(), "id", pf.GetId(), "org", pf.GetOwner().GetOrgId())
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@ package generate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/generate"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
@@ -19,6 +22,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
cmd.AddCommand(NewPlatform(cfg))
|
||||
cmd.AddCommand(NewComponent())
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -31,11 +35,70 @@ func NewPlatform(cfg *holos.Config) *cobra.Command {
|
||||
cmd.Args = cobra.ExactArgs(1)
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
clientContext := holos.NewClientContext(ctx)
|
||||
client := client.New(client.NewConfig(cfg))
|
||||
|
||||
for _, name := range args {
|
||||
if err := generate.GeneratePlatform(ctx, name); err != nil {
|
||||
if err := generate.GeneratePlatform(ctx, client, clientContext.OrgID, name); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// NewComponent returns a command to generate a holos component
|
||||
func NewComponent() *cobra.Command {
|
||||
cmd := command.New("component")
|
||||
cmd.Short = "generate a component from an embedded schematic"
|
||||
|
||||
cmd.AddCommand(NewCueComponent())
|
||||
cmd.AddCommand(NewHelmComponent())
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewHelmComponent() *cobra.Command {
|
||||
cmd := command.New("helm")
|
||||
cmd.Short = "generate a helm component from a schematic"
|
||||
|
||||
for _, name := range generate.HelmComponents() {
|
||||
cmd.AddCommand(makeSchematicCommand("helm", name))
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewCueComponent() *cobra.Command {
|
||||
cmd := command.New("cue")
|
||||
cmd.Short = "generate a cue component from a schematic"
|
||||
|
||||
for _, name := range generate.CueComponents() {
|
||||
cmd.AddCommand(makeSchematicCommand("cue", name))
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func makeSchematicCommand(kind, name string) *cobra.Command {
|
||||
cmd := command.New(name)
|
||||
cfg, err := generate.NewSchematic(filepath.Join("components", kind), name)
|
||||
if err != nil {
|
||||
slog.Error("could not get schematic", "err", err)
|
||||
return nil
|
||||
}
|
||||
cmd.Short = cfg.Short
|
||||
cmd.Long = cfg.Long
|
||||
cmd.Args = cobra.NoArgs
|
||||
cmd.Flags().AddGoFlagSet(cfg.FlagSet())
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
if err := generate.GenerateComponent(ctx, kind, name, cfg); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
82
internal/cli/pull/pull.go
Normal file
82
internal/cli/pull/pull.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Package pull pulls resources from the PlatformService and caches them in the
|
||||
// local filesystem.
|
||||
package pull
|
||||
|
||||
import (
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("pull")
|
||||
cmd.Short = "pull resources from holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
cmd.AddCommand(NewPlatform(config))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatform(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("platform")
|
||||
|
||||
cmd.Short = "pull platform resources"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
cmd.AddCommand(NewPlatformConfig(cfg))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatformConfig(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("model")
|
||||
cmd.Aliases = []string{"config"}
|
||||
cmd.Short = "pull platform model"
|
||||
cmd.Args = cobra.MinimumNArgs(1)
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
if ctx == nil {
|
||||
return errors.Wrap(errors.New("cannot execute: no context"))
|
||||
}
|
||||
ctx = logger.NewContext(ctx, logger.FromContext(ctx).With("server", cfg.Client().Server()))
|
||||
rpc := client.New(cfg)
|
||||
for _, name := range args {
|
||||
// Get the platform metadata for the platform id.
|
||||
pmd, err := client.LoadPlatform(ctx, name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log := logger.FromContext(ctx).With("platform_id", pmd.GetId())
|
||||
// Get the platform model
|
||||
model, err := rpc.PlatformModel(ctx, pmd.GetId())
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.Info("pulled platform model")
|
||||
// Build the PlatformConfig
|
||||
pc := &object.PlatformConfig{
|
||||
PlatformId: pmd.GetId(),
|
||||
PlatformModel: model,
|
||||
}
|
||||
// Save the PlatformConfig
|
||||
path, err := client.SavePlatformConfig(ctx, name, pc)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.Info("saved platform config", "path", path)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
107
internal/cli/push/push.go
Normal file
107
internal/cli/push/push.go
Normal file
@@ -0,0 +1,107 @@
|
||||
// Package push pushes resources to the holos api server.
|
||||
package push
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/push"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("push")
|
||||
cmd.Short = "push resources to holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
cmd.AddCommand(NewPlatform(config))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatform(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("platform")
|
||||
|
||||
cmd.Short = "push platform resources to holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
cmd.AddCommand(NewPlatformForm(cfg))
|
||||
cmd.AddCommand(NewPlatformModel(cfg))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatformForm(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("form")
|
||||
cmd.Short = "push platform form to holos server"
|
||||
cmd.Args = cobra.MinimumNArgs(1)
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
if ctx == nil {
|
||||
return errors.Wrap(errors.New("cannot execute: no context"))
|
||||
}
|
||||
ctx = logger.NewContext(ctx, logger.FromContext(ctx).With("server", cfg.Client().Server()))
|
||||
rpc := client.New(cfg)
|
||||
for _, name := range args {
|
||||
// Get the platform metadata for the platform id.
|
||||
p, err := client.LoadPlatform(ctx, name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Build the form from the cue code.
|
||||
form, err := push.PlatformForm(ctx, name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// Make the rpc call to update the platform form.
|
||||
if err := rpc.UpdateForm(ctx, p.GetId(), form); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
slog.Default().InfoContext(ctx, fmt.Sprintf("pushed: %s/ui/platform/%s", cfg.Client().Server(), p.GetId()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatformModel(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("model")
|
||||
cmd.Short = "push platform model to holos server"
|
||||
cmd.Args = cobra.MinimumNArgs(1)
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
if ctx == nil {
|
||||
return errors.Wrap(errors.New("cannot execute: no context"))
|
||||
}
|
||||
ctx = logger.NewContext(ctx, logger.FromContext(ctx).With("server", cfg.Client().Server()))
|
||||
rpc := client.New(cfg)
|
||||
for _, name := range args {
|
||||
// Get the platform config for the platform id.
|
||||
p, err := client.LoadPlatformConfig(ctx, name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Make the rpc call to update the platform form.
|
||||
if err := rpc.UpdatePlatformModel(ctx, p.PlatformId, p.PlatformModel); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
slog.Default().InfoContext(ctx, fmt.Sprintf("pushed: %s/ui/platform/%s", cfg.Client().Server(), p.PlatformId))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
// Package register provides user registration via the command line.
|
||||
package register
|
||||
|
||||
import (
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/register"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -13,7 +15,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd.Short = "register with holos server"
|
||||
cmd.Args = cobra.NoArgs
|
||||
|
||||
config := register.NewConfig(cfg)
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
@@ -23,7 +25,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
}
|
||||
|
||||
// NewUser returns a command to register a user with holos server.
|
||||
func NewUser(cfg *register.Config) *cobra.Command {
|
||||
func NewUser(cfg *client.Config) *cobra.Command {
|
||||
cmd := command.New("user")
|
||||
cmd.Short = "user registration workflow"
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
@@ -4,40 +4,50 @@ import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/holos-run/holos/internal/builder"
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
"github.com/holos-run/holos/internal/render"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// New returns the render subcommand for the root command
|
||||
func New(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("render [directory...]")
|
||||
cmd := command.New("render")
|
||||
cmd.Args = cobra.NoArgs
|
||||
cmd.Short = "render platform configuration"
|
||||
cmd.AddCommand(NewComponent(cfg))
|
||||
cmd.AddCommand(NewPlatform(cfg))
|
||||
return cmd
|
||||
}
|
||||
|
||||
// New returns the component subcommand for the render command
|
||||
func NewComponent(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("component [directory...]")
|
||||
cmd.Args = cobra.MinimumNArgs(1)
|
||||
cmd.Short = "write kubernetes api objects to the filesystem"
|
||||
cmd.Flags().SortFlags = false
|
||||
cmd.Flags().AddGoFlagSet(cfg.WriteFlagSet())
|
||||
cmd.Flags().AddGoFlagSet(cfg.ClusterFlagSet())
|
||||
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
var printInstances bool
|
||||
flagSet := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
flagSet.BoolVar(&printInstances, "print-instances", false, "expand /... paths for xargs")
|
||||
cmd.Flags().AddGoFlagSet(flagSet)
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
if cfg.ClusterName() == "" {
|
||||
return errors.Wrap(fmt.Errorf("missing cluster name"))
|
||||
}
|
||||
|
||||
ctx := cmd.Context()
|
||||
log := logger.FromContext(ctx).With("cluster", cfg.ClusterName())
|
||||
ctx := cmd.Root().Context()
|
||||
build := builder.New(builder.Entrypoints(args), builder.Cluster(cfg.ClusterName()))
|
||||
|
||||
if printInstances {
|
||||
instances, err := build.Instances(ctx)
|
||||
instances, err := build.Instances(ctx, config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
@@ -47,35 +57,72 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
return nil
|
||||
}
|
||||
|
||||
results, err := build.Run(cmd.Context())
|
||||
results, err := build.Run(ctx, config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
// TODO: Avoid accidental over-writes if to holos component instances result in
|
||||
// the same file path. Write files into a blank temporary directory, error if a
|
||||
// file exists, then move the directory into place.
|
||||
// TODO: Avoid accidental over-writes if two or more holos component
|
||||
// instances result in the same file path. Write files into a blank
|
||||
// temporary directory, error if a file exists, then move the directory into
|
||||
// place.
|
||||
var result Result
|
||||
for _, result = range results {
|
||||
log := logger.FromContext(ctx).With(
|
||||
"cluster", cfg.ClusterName(),
|
||||
"name", result.Name(),
|
||||
)
|
||||
if result.Continue() {
|
||||
continue
|
||||
}
|
||||
// DeployFiles from the BuildPlan
|
||||
if err := result.WriteDeployFiles(ctx, cfg.WriteTo()); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// API Objects
|
||||
path := result.Filename(cfg.WriteTo(), cfg.ClusterName())
|
||||
if err := result.Save(ctx, path, result.AccumulatedOutput()); err != nil {
|
||||
return errors.Wrap(err)
|
||||
if result.SkipWriteAccumulatedOutput() {
|
||||
log.DebugContext(ctx, "skipped writing k8s objects for "+result.Name())
|
||||
} else {
|
||||
path := result.Filename(cfg.WriteTo(), cfg.ClusterName())
|
||||
if err := result.Save(ctx, path, result.AccumulatedOutput()); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
}
|
||||
// Kustomization
|
||||
path = result.KustomizationFilename(cfg.WriteTo(), cfg.ClusterName())
|
||||
if err := result.Save(ctx, path, result.KustomizationContent()); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.InfoContext(ctx, "rendered "+result.Name(), "status", "ok", "action", "rendered", "name", result.Name())
|
||||
|
||||
log.InfoContext(ctx, "rendered "+result.Name(), "status", "ok", "action", "rendered")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewPlatform(cfg *holos.Config) *cobra.Command {
|
||||
cmd := command.New("platform [directory]")
|
||||
cmd.Args = cobra.ExactArgs(1)
|
||||
cmd.Short = "render all platform components"
|
||||
|
||||
config := client.NewConfig(cfg)
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.ClientFlagSet())
|
||||
cmd.PersistentFlags().AddGoFlagSet(config.TokenFlagSet())
|
||||
|
||||
var concurrency int
|
||||
cmd.Flags().IntVar(&concurrency, "concurrency", min(runtime.NumCPU(), 8), "Number of concurrent components to render")
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
build := builder.New(builder.Entrypoints(args))
|
||||
|
||||
platform, err := build.Platform(ctx, config)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return render.Platform(ctx, concurrency, platform, cmd.ErrOrStderr())
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
type Result interface {
|
||||
Continue() bool
|
||||
Name() string
|
||||
@@ -83,5 +130,8 @@ type Result interface {
|
||||
KustomizationFilename(writeTo string, cluster string) string
|
||||
Save(ctx context.Context, path string, content string) error
|
||||
AccumulatedOutput() string
|
||||
KustomizationContent() string
|
||||
SkipWriteAccumulatedOutput() bool
|
||||
WriteDeployFiles(ctx context.Context, writeTo string) error
|
||||
GetKind() string
|
||||
GetAPIVersion() string
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"github.com/holos-run/holos/internal/server"
|
||||
|
||||
"github.com/holos-run/holos/internal/cli/build"
|
||||
"github.com/holos-run/holos/internal/cli/command"
|
||||
"github.com/holos-run/holos/internal/cli/controller"
|
||||
"github.com/holos-run/holos/internal/cli/create"
|
||||
"github.com/holos-run/holos/internal/cli/generate"
|
||||
@@ -20,6 +22,8 @@ import (
|
||||
"github.com/holos-run/holos/internal/cli/login"
|
||||
"github.com/holos-run/holos/internal/cli/logout"
|
||||
"github.com/holos-run/holos/internal/cli/preflight"
|
||||
"github.com/holos-run/holos/internal/cli/pull"
|
||||
"github.com/holos-run/holos/internal/cli/push"
|
||||
"github.com/holos-run/holos/internal/cli/register"
|
||||
"github.com/holos-run/holos/internal/cli/render"
|
||||
"github.com/holos-run/holos/internal/cli/rpc"
|
||||
@@ -32,7 +36,7 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "holos",
|
||||
Short: "holos manages a holistic integrated software development platform",
|
||||
Version: version.Version,
|
||||
Version: version.GetVersion(),
|
||||
Args: cobra.NoArgs,
|
||||
CompletionOptions: cobra.CompletionOptions{
|
||||
HiddenDefaultCmd: true, // Don't complete the complete subcommand itself
|
||||
@@ -70,6 +74,9 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
rootCmd.AddCommand(rpc.New(cfg))
|
||||
rootCmd.AddCommand(generate.New(cfg))
|
||||
rootCmd.AddCommand(register.New(cfg))
|
||||
rootCmd.AddCommand(pull.New(cfg))
|
||||
rootCmd.AddCommand(push.New(cfg))
|
||||
rootCmd.AddCommand(newOrgCmd())
|
||||
|
||||
// Maybe not needed?
|
||||
rootCmd.AddCommand(txtar.New(cfg))
|
||||
@@ -85,3 +92,15 @@ func New(cfg *holos.Config) *cobra.Command {
|
||||
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
func newOrgCmd() (cmd *cobra.Command) {
|
||||
cmd = command.New("orgid")
|
||||
cmd.Short = "print the current context org id."
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctx := cmd.Root().Context()
|
||||
cc := holos.NewClientContext(ctx)
|
||||
_, err := fmt.Fprintln(cmd.OutOrStdout(), cc.OrgID)
|
||||
return err
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
# Want no hash appended
|
||||
holos create secret test --namespace holos-system --from-file $WORK/test --append-hash=false
|
||||
stderr ' created: test '
|
||||
stderr ' secret=test '
|
||||
|
||||
-- test --
|
||||
sekret
|
||||
143
internal/client/client.go
Normal file
143
internal/client/client.go
Normal file
@@ -0,0 +1,143 @@
|
||||
// Package client provides configuration and convenience methods for making API calls to the holos server.
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"time"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
"github.com/holos-run/holos/internal/token"
|
||||
object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
|
||||
"github.com/holos-run/holos/service/gen/holos/organization/v1alpha1/organizationconnect"
|
||||
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
|
||||
"github.com/holos-run/holos/service/gen/holos/platform/v1alpha1/platformconnect"
|
||||
"github.com/holos-run/holos/service/gen/holos/user/v1alpha1/userconnect"
|
||||
"google.golang.org/protobuf/types/known/fieldmaskpb"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
type PlatformMutation struct {
|
||||
Name string
|
||||
DisplayName string
|
||||
flagSet *flag.FlagSet
|
||||
}
|
||||
|
||||
func (pm *PlatformMutation) FlagSet() *flag.FlagSet {
|
||||
if pm == nil {
|
||||
return nil
|
||||
}
|
||||
if pm.flagSet != nil {
|
||||
return pm.flagSet
|
||||
}
|
||||
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fs.StringVar(&pm.Name, "name", "example", "platform name")
|
||||
fs.StringVar(&pm.DisplayName, "display-name", "Example Platform", "platform display name")
|
||||
pm.flagSet = fs
|
||||
return fs
|
||||
}
|
||||
|
||||
func New(cfg *Config) *Client {
|
||||
t := token.NewClient(cfg.Token())
|
||||
s := cfg.Client().Server()
|
||||
return &Client{
|
||||
cfg: cfg,
|
||||
usrSvc: userconnect.NewUserServiceClient(t, s),
|
||||
orgSvc: organizationconnect.NewOrganizationServiceClient(t, s),
|
||||
pltSvc: platformconnect.NewPlatformServiceClient(t, s),
|
||||
}
|
||||
}
|
||||
|
||||
// Client provides convenience methods for making API calls to the holos server.
|
||||
type Client struct {
|
||||
cfg *Config
|
||||
usrSvc userconnect.UserServiceClient
|
||||
pltSvc platformconnect.PlatformServiceClient
|
||||
orgSvc organizationconnect.OrganizationServiceClient
|
||||
}
|
||||
|
||||
func (c *Client) Platforms(ctx context.Context, orgID string) ([]*platform.Platform, error) {
|
||||
if c == nil {
|
||||
return nil, errors.New("no service client")
|
||||
}
|
||||
req := &platform.ListPlatformsRequest{
|
||||
OrgId: orgID,
|
||||
FieldMask: &fieldmaskpb.FieldMask{
|
||||
Paths: []string{"id", "name", "displayName"},
|
||||
},
|
||||
}
|
||||
resp, err := c.pltSvc.ListPlatforms(ctx, connect.NewRequest(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Msg.GetPlatforms(), nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdateForm(ctx context.Context, platformID string, form *object.Form) error {
|
||||
start := time.Now()
|
||||
req := &platform.UpdatePlatformRequest{
|
||||
PlatformId: platformID,
|
||||
Update: &platform.PlatformMutation{Form: form},
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"form"}},
|
||||
}
|
||||
_, err := c.pltSvc.UpdatePlatform(ctx, connect.NewRequest(req))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "updated platform", "platform_id", platformID, "duration", time.Since(start))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) UpdatePlatformModel(ctx context.Context, platformID string, model *structpb.Struct) error {
|
||||
start := time.Now()
|
||||
req := &platform.UpdatePlatformRequest{
|
||||
PlatformId: platformID,
|
||||
Update: &platform.PlatformMutation{Model: model},
|
||||
UpdateMask: &fieldmaskpb.FieldMask{Paths: []string{"model"}},
|
||||
}
|
||||
_, err := c.pltSvc.UpdatePlatform(ctx, connect.NewRequest(req))
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "updated platform", "platform_id", platformID, "duration", time.Since(start))
|
||||
return nil
|
||||
}
|
||||
|
||||
// PlatformModel gets the platform model from the PlatformService.
|
||||
func (c *Client) PlatformModel(ctx context.Context, platformID string) (*structpb.Struct, error) {
|
||||
start := time.Now()
|
||||
req := &platform.GetPlatformRequest{
|
||||
PlatformId: platformID,
|
||||
FieldMask: &fieldmaskpb.FieldMask{Paths: []string{"spec.model"}},
|
||||
}
|
||||
pf, err := c.pltSvc.GetPlatform(ctx, connect.NewRequest(req))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
log := logger.FromContext(ctx)
|
||||
log.DebugContext(ctx, "get platform", "platform_id", platformID, "duration", time.Since(start))
|
||||
return pf.Msg.GetPlatform().GetSpec().GetModel(), nil
|
||||
}
|
||||
|
||||
func (c *Client) CreatePlatform(ctx context.Context, pm PlatformMutation) (*platform.Platform, error) {
|
||||
log := logger.FromContext(ctx).With("platform", pm.Name)
|
||||
start := time.Now()
|
||||
req := &platform.CreatePlatformRequest{
|
||||
OrgId: c.cfg.context.OrgID,
|
||||
Create: &platform.PlatformMutation{
|
||||
Name: &pm.Name,
|
||||
DisplayName: &pm.DisplayName,
|
||||
},
|
||||
}
|
||||
pf, err := c.pltSvc.CreatePlatform(ctx, connect.NewRequest(req))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
log = log.With("platform_id", pf.Msg.GetPlatform().GetId())
|
||||
log.DebugContext(ctx, "create platform", "duration", time.Since(start))
|
||||
return pf.Msg.GetPlatform(), nil
|
||||
}
|
||||
71
internal/client/config.go
Normal file
71
internal/client/config.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Package client provides client configuration for the holos cli.
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
|
||||
"github.com/holos-run/holos/internal/holos"
|
||||
"github.com/holos-run/holos/internal/token"
|
||||
)
|
||||
|
||||
func NewConfig(cfg *holos.Config) *Config {
|
||||
return &Config{
|
||||
holos: cfg,
|
||||
client: holos.NewClientConfig(),
|
||||
context: holos.NewClientContext(context.Background()),
|
||||
token: token.NewConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
holos *holos.Config
|
||||
client *holos.ClientConfig
|
||||
context *holos.ClientContext
|
||||
token *token.Config
|
||||
}
|
||||
|
||||
func (c *Config) ClientFlagSet() *flag.FlagSet {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.client.FlagSet()
|
||||
}
|
||||
|
||||
func (c *Config) TokenFlagSet() *flag.FlagSet {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.token.FlagSet()
|
||||
}
|
||||
|
||||
func (c *Config) Token() *token.Config {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.token
|
||||
}
|
||||
|
||||
func (c *Config) Client() *holos.ClientConfig {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.client
|
||||
}
|
||||
|
||||
// Context returns the ClientContext useful to get the OrgID and UserID for rpc
|
||||
// calls.
|
||||
func (c *Config) Context() *holos.ClientContext {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.context
|
||||
}
|
||||
|
||||
// Holos returns the *holos.Config
|
||||
func (c *Config) Holos() *holos.Config {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.holos
|
||||
}
|
||||
69
internal/client/platform.go
Normal file
69
internal/client/platform.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
object "github.com/holos-run/holos/service/gen/holos/object/v1alpha1"
|
||||
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
|
||||
// PlatformMetadataFile is the platform metadata json file name located in the root
|
||||
// of a platform directory.
|
||||
const PlatformMetadataFile = "platform.metadata.json"
|
||||
|
||||
// PlatformConfigFile is the marshaled json representation of the PlatformConfig
|
||||
// DTO used to cache the data holos passes from the PlatformService to CUE when
|
||||
// rendering platform components.
|
||||
const PlatformConfigFile = "platform.config.json"
|
||||
|
||||
// LoadPlatform loads the platform.metadata.json file from a named path. Useful
|
||||
// to obtain a platform id for PlatformService rpc methods.
|
||||
func LoadPlatform(ctx context.Context, name string) (*platform.Platform, error) {
|
||||
data, err := os.ReadFile(filepath.Join(name, PlatformMetadataFile))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load platform metadata: %w", err)
|
||||
}
|
||||
p := &platform.Platform{}
|
||||
if err := protojson.Unmarshal(data, p); err != nil {
|
||||
return nil, fmt.Errorf("could not load platform metadata: %w", err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// LoadPlatformConfig loads the PlatformConfig DTO from the platform.config.json
|
||||
// file. Useful to provide all values necessary to render cue config without an
|
||||
// rpc to the HolosService.
|
||||
func LoadPlatformConfig(ctx context.Context, name string) (*object.PlatformConfig, error) {
|
||||
data, err := os.ReadFile(filepath.Join(name, PlatformConfigFile))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load platform config: %w", err)
|
||||
}
|
||||
pc := &object.PlatformConfig{}
|
||||
if err := protojson.Unmarshal(data, pc); err != nil {
|
||||
return nil, fmt.Errorf("could not load platform config: %w", err)
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// SavePlatformConfig writes pc to the platform root directory path identified by name.
|
||||
func SavePlatformConfig(ctx context.Context, name string, pc *object.PlatformConfig) (string, error) {
|
||||
encoder := protojson.MarshalOptions{Multiline: true}
|
||||
data, err := encoder.Marshal(pc)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(data) > 0 {
|
||||
data = append(data, '\n')
|
||||
}
|
||||
path := filepath.Join(name, PlatformConfigFile)
|
||||
if err := os.WriteFile(path, data, 0644); err != nil {
|
||||
return "", fmt.Errorf("could not write platform config: %w", err)
|
||||
}
|
||||
logger.FromContext(ctx).DebugContext(ctx, "wrote", "path", path)
|
||||
return path, nil
|
||||
}
|
||||
525
internal/frontend/holos/package-lock.json
generated
525
internal/frontend/holos/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -21,9 +21,9 @@
|
||||
"@angular/platform-browser": "^17.3.0",
|
||||
"@angular/platform-browser-dynamic": "^17.3.0",
|
||||
"@angular/router": "^17.3.0",
|
||||
"@bufbuild/protobuf": "^1.9.0",
|
||||
"@bufbuild/protobuf": "^1.10.0",
|
||||
"@connectrpc/connect": "^1.4.0",
|
||||
"@connectrpc/connect-query": "^1.4.0",
|
||||
"@connectrpc/connect-query": "^1.4.1",
|
||||
"@connectrpc/connect-web": "^1.4.0",
|
||||
"@ngx-formly/core": "^6.3.0",
|
||||
"@ngx-formly/material": "^6.3.0",
|
||||
@@ -40,10 +40,10 @@
|
||||
"@angular-eslint/template-parser": "17.3.0",
|
||||
"@angular/cli": "^17.3.4",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@bufbuild/buf": "^1.32.0",
|
||||
"@bufbuild/protoc-gen-es": "^1.9.0",
|
||||
"@bufbuild/buf": "^1.34.0",
|
||||
"@bufbuild/protoc-gen-es": "^1.10.0",
|
||||
"@connectrpc/protoc-gen-connect-es": "^1.4.0",
|
||||
"@connectrpc/protoc-gen-connect-query": "^1.4.0",
|
||||
"@connectrpc/protoc-gen-connect-query": "^1.4.1",
|
||||
"@ngx-formly/schematics": "^6.3.0",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "7.2.0",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/object/v1alpha1/object.proto (package holos.object.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3, Timestamp } from "@bufbuild/protobuf";
|
||||
import { Message, proto3, Struct, Timestamp } from "@bufbuild/protobuf";
|
||||
|
||||
/**
|
||||
* @generated from message holos.object.v1alpha1.Detail
|
||||
@@ -325,3 +325,95 @@ export class ResourceOwner extends Message<ResourceOwner> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form represents a Formly json powered form.
|
||||
*
|
||||
* @generated from message holos.object.v1alpha1.Form
|
||||
*/
|
||||
export class Form extends Message<Form> {
|
||||
/**
|
||||
* fields represents FormlyFieldConfig[] encoded as an array of JSON objects
|
||||
* organized by section.
|
||||
*
|
||||
* @generated from field: repeated google.protobuf.Struct field_configs = 1;
|
||||
*/
|
||||
fieldConfigs: Struct[] = [];
|
||||
|
||||
constructor(data?: PartialMessage<Form>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.object.v1alpha1.Form";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "field_configs", kind: "message", T: Struct, repeated: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Form {
|
||||
return new Form().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Form {
|
||||
return new Form().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Form {
|
||||
return new Form().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: Form | PlainMessage<Form> | undefined, b: Form | PlainMessage<Form> | undefined): boolean {
|
||||
return proto3.util.equals(Form, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PlatformConfig represents the data passed from the holos cli to CUE when
|
||||
* rendering configuration.
|
||||
*
|
||||
* @generated from message holos.object.v1alpha1.PlatformConfig
|
||||
*/
|
||||
export class PlatformConfig extends Message<PlatformConfig> {
|
||||
/**
|
||||
* Platform UUID.
|
||||
*
|
||||
* @generated from field: string platform_id = 1;
|
||||
*/
|
||||
platformId = "";
|
||||
|
||||
/**
|
||||
* Platform Model.
|
||||
*
|
||||
* @generated from field: google.protobuf.Struct platform_model = 2;
|
||||
*/
|
||||
platformModel?: Struct;
|
||||
|
||||
constructor(data?: PartialMessage<PlatformConfig>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.object.v1alpha1.PlatformConfig";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "platform_model", kind: "message", T: Struct },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformConfig {
|
||||
return new PlatformConfig().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PlatformConfig {
|
||||
return new PlatformConfig().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PlatformConfig {
|
||||
return new PlatformConfig().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: PlatformConfig | PlainMessage<PlatformConfig> | undefined, b: PlatformConfig | PlainMessage<PlatformConfig> | undefined): boolean {
|
||||
return proto3.util.equals(PlatformConfig, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/organization/v1alpha1/organization.proto (package holos.organization.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/organization/v1alpha1/organization_service.proto (package holos.organization.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/platform/v1alpha1/platform.proto (package holos.platform.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { Message, proto3, Struct } from "@bufbuild/protobuf";
|
||||
import { Detail } from "../../object/v1alpha1/object_pb.js";
|
||||
import { Detail, Form } from "../../object/v1alpha1/object_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from message holos.platform.v1alpha1.Platform
|
||||
@@ -147,7 +147,7 @@ export class Spec extends Message<Spec> {
|
||||
model?: Struct;
|
||||
|
||||
/**
|
||||
* @generated from field: optional holos.platform.v1alpha1.Form form = 2;
|
||||
* @generated from field: optional holos.object.v1alpha1.Form form = 2;
|
||||
*/
|
||||
form?: Form;
|
||||
|
||||
@@ -180,45 +180,3 @@ export class Spec extends Message<Spec> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Form represents the Formly input form.
|
||||
*
|
||||
* @generated from message holos.platform.v1alpha1.Form
|
||||
*/
|
||||
export class Form extends Message<Form> {
|
||||
/**
|
||||
* fields represents FormlyFieldConfig[] encoded as an array of JSON objects
|
||||
* organized by section.
|
||||
*
|
||||
* @generated from field: repeated google.protobuf.Struct field_configs = 1;
|
||||
*/
|
||||
fieldConfigs: Struct[] = [];
|
||||
|
||||
constructor(data?: PartialMessage<Form>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.platform.v1alpha1.Form";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "field_configs", kind: "message", T: Struct, repeated: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): Form {
|
||||
return new Form().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): Form {
|
||||
return new Form().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): Form {
|
||||
return new Form().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: Form | PlainMessage<Form> | undefined, b: Form | PlainMessage<Form> | undefined): boolean {
|
||||
return proto3.util.equals(Form, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/platform/v1alpha1/platform_service.proto (package holos.platform.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
|
||||
import { FieldMask, Message, proto3, Struct } from "@bufbuild/protobuf";
|
||||
import { Form, Platform } from "./platform_pb.js";
|
||||
import { Platform } from "./platform_pb.js";
|
||||
import { Form } from "../../object/v1alpha1/object_pb.js";
|
||||
|
||||
/**
|
||||
* @generated from message holos.platform.v1alpha1.CreatePlatformRequest
|
||||
*/
|
||||
export class CreatePlatformRequest extends Message<CreatePlatformRequest> {
|
||||
/**
|
||||
* @generated from field: holos.platform.v1alpha1.Platform platform = 1;
|
||||
* @generated from field: string org_id = 1;
|
||||
*/
|
||||
platform?: Platform;
|
||||
orgId = "";
|
||||
|
||||
/**
|
||||
* @generated from field: holos.platform.v1alpha1.PlatformMutation create = 2;
|
||||
*/
|
||||
create?: PlatformMutation;
|
||||
|
||||
constructor(data?: PartialMessage<CreatePlatformRequest>) {
|
||||
super();
|
||||
@@ -24,7 +30,8 @@ export class CreatePlatformRequest extends Message<CreatePlatformRequest> {
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.platform.v1alpha1.CreatePlatformRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "platform", kind: "message", T: Platform },
|
||||
{ no: 1, name: "org_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "create", kind: "message", T: PlatformMutation },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): CreatePlatformRequest {
|
||||
@@ -167,20 +174,27 @@ export class GetPlatformResponse extends Message<GetPlatformResponse> {
|
||||
* @generated from message holos.platform.v1alpha1.UpdatePlatformRequest
|
||||
*/
|
||||
export class UpdatePlatformRequest extends Message<UpdatePlatformRequest> {
|
||||
/**
|
||||
* Platform UUID to update.
|
||||
*
|
||||
* @generated from field: string platform_id = 1;
|
||||
*/
|
||||
platformId = "";
|
||||
|
||||
/**
|
||||
* Update operations to perform. Fields are set to the provided value if
|
||||
* selected by the mask. Absent fields are cleared if they are selected by
|
||||
* the mask.
|
||||
*
|
||||
* @generated from field: holos.platform.v1alpha1.UpdatePlatformOperation update = 1;
|
||||
* @generated from field: holos.platform.v1alpha1.PlatformMutation update = 2;
|
||||
*/
|
||||
update?: UpdatePlatformOperation;
|
||||
update?: PlatformMutation;
|
||||
|
||||
/**
|
||||
* FieldMask represents the mutation operations to perform. Marked optional
|
||||
* for the nil guard check. Required.
|
||||
*
|
||||
* @generated from field: optional google.protobuf.FieldMask update_mask = 2;
|
||||
* @generated from field: optional google.protobuf.FieldMask update_mask = 3;
|
||||
*/
|
||||
updateMask?: FieldMask;
|
||||
|
||||
@@ -192,8 +206,9 @@ export class UpdatePlatformRequest extends Message<UpdatePlatformRequest> {
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.platform.v1alpha1.UpdatePlatformRequest";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "update", kind: "message", T: UpdatePlatformOperation },
|
||||
{ no: 2, name: "update_mask", kind: "message", T: FieldMask, opt: true },
|
||||
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "update", kind: "message", T: PlatformMutation },
|
||||
{ no: 3, name: "update_mask", kind: "message", T: FieldMask, opt: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): UpdatePlatformRequest {
|
||||
@@ -333,16 +348,11 @@ export class ListPlatformsResponse extends Message<ListPlatformsResponse> {
|
||||
}
|
||||
|
||||
/**
|
||||
* @generated from message holos.platform.v1alpha1.UpdatePlatformOperation
|
||||
* PlatformMutation represents the fields to create or update.
|
||||
*
|
||||
* @generated from message holos.platform.v1alpha1.PlatformMutation
|
||||
*/
|
||||
export class UpdatePlatformOperation extends Message<UpdatePlatformOperation> {
|
||||
/**
|
||||
* Platform UUID to update.
|
||||
*
|
||||
* @generated from field: string platform_id = 1;
|
||||
*/
|
||||
platformId = "";
|
||||
|
||||
export class PlatformMutation extends Message<PlatformMutation> {
|
||||
/**
|
||||
* Update the platform name.
|
||||
*
|
||||
@@ -367,39 +377,38 @@ export class UpdatePlatformOperation extends Message<UpdatePlatformOperation> {
|
||||
/**
|
||||
* Replace the form.
|
||||
*
|
||||
* @generated from field: optional holos.platform.v1alpha1.Form form = 5;
|
||||
* @generated from field: optional holos.object.v1alpha1.Form form = 5;
|
||||
*/
|
||||
form?: Form;
|
||||
|
||||
constructor(data?: PartialMessage<UpdatePlatformOperation>) {
|
||||
constructor(data?: PartialMessage<PlatformMutation>) {
|
||||
super();
|
||||
proto3.util.initPartial(data, this);
|
||||
}
|
||||
|
||||
static readonly runtime: typeof proto3 = proto3;
|
||||
static readonly typeName = "holos.platform.v1alpha1.UpdatePlatformOperation";
|
||||
static readonly typeName = "holos.platform.v1alpha1.PlatformMutation";
|
||||
static readonly fields: FieldList = proto3.util.newFieldList(() => [
|
||||
{ no: 1, name: "platform_id", kind: "scalar", T: 9 /* ScalarType.STRING */ },
|
||||
{ no: 2, name: "name", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true },
|
||||
{ no: 3, name: "display_name", kind: "scalar", T: 9 /* ScalarType.STRING */, opt: true },
|
||||
{ no: 4, name: "model", kind: "message", T: Struct, opt: true },
|
||||
{ no: 5, name: "form", kind: "message", T: Form, opt: true },
|
||||
]);
|
||||
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): UpdatePlatformOperation {
|
||||
return new UpdatePlatformOperation().fromBinary(bytes, options);
|
||||
static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PlatformMutation {
|
||||
return new PlatformMutation().fromBinary(bytes, options);
|
||||
}
|
||||
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): UpdatePlatformOperation {
|
||||
return new UpdatePlatformOperation().fromJson(jsonValue, options);
|
||||
static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PlatformMutation {
|
||||
return new PlatformMutation().fromJson(jsonValue, options);
|
||||
}
|
||||
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): UpdatePlatformOperation {
|
||||
return new UpdatePlatformOperation().fromJsonString(jsonString, options);
|
||||
static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PlatformMutation {
|
||||
return new PlatformMutation().fromJsonString(jsonString, options);
|
||||
}
|
||||
|
||||
static equals(a: UpdatePlatformOperation | PlainMessage<UpdatePlatformOperation> | undefined, b: UpdatePlatformOperation | PlainMessage<UpdatePlatformOperation> | undefined): boolean {
|
||||
return proto3.util.equals(UpdatePlatformOperation, a, b);
|
||||
static equals(a: PlatformMutation | PlainMessage<PlatformMutation> | undefined, b: PlatformMutation | PlainMessage<PlatformMutation> | undefined): boolean {
|
||||
return proto3.util.equals(PlatformMutation, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/storage/v1alpha1/storage.proto (package holos.storage.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/system/v1alpha1/system.proto (package holos.system.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/system/v1alpha1/system_service.proto (package holos.system.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/user/v1alpha1/user.proto (package holos.user.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// @generated by protoc-gen-es v1.9.0 with parameter "target=ts"
|
||||
// @generated by protoc-gen-es v1.10.0 with parameter "target=ts"
|
||||
// @generated from file holos/user/v1alpha1/user_service.proto (package holos.user.v1alpha1, syntax proto3)
|
||||
/* eslint-disable */
|
||||
// @ts-nocheck
|
||||
|
||||
@@ -5,7 +5,7 @@ import { ObservableClient } from '../../connect/observable-client';
|
||||
import { Organization } from '../gen/holos/organization/v1alpha1/organization_pb';
|
||||
import { Platform } from '../gen/holos/platform/v1alpha1/platform_pb';
|
||||
import { PlatformService as ConnectPlatformService } from '../gen/holos/platform/v1alpha1/platform_service_connect';
|
||||
import { GetPlatformRequest, ListPlatformsRequest, UpdatePlatformOperation, UpdatePlatformRequest } from '../gen/holos/platform/v1alpha1/platform_service_pb';
|
||||
import { GetPlatformRequest, ListPlatformsRequest, PlatformMutation, UpdatePlatformRequest } from '../gen/holos/platform/v1alpha1/platform_service_pb';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -26,9 +26,9 @@ export class PlatformService {
|
||||
}
|
||||
|
||||
updateModel(platformId: string, model: JsonValue): Observable<Platform | undefined> {
|
||||
const update = new UpdatePlatformOperation({ platformId: platformId, model: Struct.fromJson(model) })
|
||||
const update = new PlatformMutation({ model: Struct.fromJson(model) })
|
||||
const updateMask = new FieldMask({ paths: ["model"] })
|
||||
const req = new UpdatePlatformRequest({ update: update, updateMask: updateMask })
|
||||
const req = new UpdatePlatformRequest({ platformId: platformId, update: update, updateMask: updateMask })
|
||||
return this.client.updatePlatform(req).pipe(
|
||||
switchMap(resp => { return of(resp.platform) })
|
||||
)
|
||||
|
||||
149
internal/generate/component.go
Normal file
149
internal/generate/component.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"io/fs"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
)
|
||||
|
||||
//go:embed all:components
|
||||
var components embed.FS
|
||||
|
||||
// componentsRoot is the root path to copy component cue code from.
|
||||
const componentsRoot = "components"
|
||||
|
||||
func NewSchematic(root string, name string) (*Schematic, error) {
|
||||
data, err := components.ReadFile(filepath.Join(root, name, "schematic.json"))
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
schematic := Schematic{Name: name}
|
||||
if err := json.Unmarshal(data, &schematic); err != nil {
|
||||
return nil, errors.Wrap(err)
|
||||
}
|
||||
return &schematic, nil
|
||||
}
|
||||
|
||||
// Schematic represents the flags and command metadata stored in the
|
||||
// schematic.yaml file along side each schematic.
|
||||
type Schematic struct {
|
||||
// Name represents the name of the resource the schematic generates.
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
|
||||
Short string `json:"short,omitempty" yaml:"short,omitempty"`
|
||||
Long string `json:"long,omitempty" yaml:"long,omitempty"`
|
||||
|
||||
Chart *string `json:"chart,omitempty" yaml:"chart,omitempty"`
|
||||
Version *string `json:"version,omitempty" yaml:"version,omitempty"`
|
||||
Namespace *string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
|
||||
RepoName *string `json:"reponame,omitempty" yaml:"reponame,omitempty"`
|
||||
RepoURL *string `json:"repourl,omitempty" yaml:"repourl,omitempty"`
|
||||
|
||||
flagSet *flag.FlagSet
|
||||
}
|
||||
|
||||
func (s *Schematic) FlagSet() *flag.FlagSet {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
if s.flagSet != nil {
|
||||
return s.flagSet
|
||||
}
|
||||
fs := flag.NewFlagSet("", flag.ContinueOnError)
|
||||
fs.StringVar(&s.Name, "name", s.Name, "component name")
|
||||
if s.Chart != nil {
|
||||
fs.StringVar(s.Chart, "chart", *s.Chart, "chart name")
|
||||
}
|
||||
if s.Version != nil {
|
||||
fs.StringVar(s.Version, "component-version", *s.Version, "component version")
|
||||
}
|
||||
if s.Namespace != nil {
|
||||
fs.StringVar(s.Namespace, "namespace", *s.Namespace, "namespace")
|
||||
}
|
||||
if s.RepoName != nil {
|
||||
fs.StringVar(s.RepoName, "repo-name", *s.RepoName, "chart repository name")
|
||||
}
|
||||
if s.RepoURL != nil {
|
||||
fs.StringVar(s.RepoURL, "repo-url", *s.RepoURL, "chart repository url")
|
||||
}
|
||||
s.flagSet = fs
|
||||
return fs
|
||||
}
|
||||
|
||||
// CueComponents returns a slice of embedded component schematics or nil if there are none.
|
||||
func CueComponents() []string {
|
||||
entries, err := fs.ReadDir(components, filepath.Join(componentsRoot, "cue"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dirs := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
dirs = append(dirs, entry.Name())
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// HelmComponents returns a slice of embedded component schematics or nil if there are none.
|
||||
func HelmComponents() []string {
|
||||
entries, err := fs.ReadDir(components, filepath.Join(componentsRoot, "helm"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dirs := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
dirs = append(dirs, entry.Name())
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// makeRenderFunc makes a template rendering function for embedded files.
|
||||
func makeRenderFunc[T any](log *slog.Logger, path string, cfg T) func([]byte) *bytes.Buffer {
|
||||
return func(content []byte) *bytes.Buffer {
|
||||
tmpl, err := template.New(filepath.Base(path)).Parse(string(content))
|
||||
if err != nil {
|
||||
log.Error("could not load template", "err", err)
|
||||
return bytes.NewBuffer(content)
|
||||
}
|
||||
|
||||
var rendered bytes.Buffer
|
||||
if err := tmpl.Execute(&rendered, cfg); err != nil {
|
||||
log.Error("could not execute template", "err", err)
|
||||
return bytes.NewBuffer(content)
|
||||
}
|
||||
|
||||
return &rendered
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateComponent writes the cue code for a component to the local working
|
||||
// directory.
|
||||
func GenerateComponent(ctx context.Context, kind string, name string, cfg *Schematic) error {
|
||||
// use name from args to build the source path
|
||||
path := filepath.Join(componentsRoot, kind, name)
|
||||
// use cfg.Name from flags to build the destination path
|
||||
dstPath := filepath.Join(getCwd(ctx), cfg.Name)
|
||||
log := logger.FromContext(ctx).With("name", cfg.Name, "path", dstPath)
|
||||
log.DebugContext(ctx, "mkdir")
|
||||
if err := os.MkdirAll(dstPath, os.ModePerm); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
mapper := makeRenderFunc(log, path, cfg)
|
||||
if err := copyEmbedFS(ctx, components, path, dstPath, mapper); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "generated component")
|
||||
return nil
|
||||
}
|
||||
4
internal/generate/components/cue/argocd/argocd.cue
Executable file
4
internal/generate/components/cue/argocd/argocd.cue
Executable file
@@ -0,0 +1,4 @@
|
||||
package holos
|
||||
|
||||
// Produce a kubectl kustomize build plan.
|
||||
(#Kustomize & {Name: "{{ .Name }}"}).Output
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: "{{ .Namespace }}"
|
||||
resources:
|
||||
- "https://raw.githubusercontent.com/argoproj/argo-cd/v{{ .Version }}/manifests/install.yaml"
|
||||
7
internal/generate/components/cue/argocd/schematic.json
Normal file
7
internal/generate/components/cue/argocd/schematic.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "argocd",
|
||||
"namespace": "argocd",
|
||||
"short": "argocd kustomize",
|
||||
"long": "Manage argocd using a kustomization.yaml build plan.",
|
||||
"version": "2.11.2"
|
||||
}
|
||||
21
internal/generate/components/cue/configmap/configmap.cue
Normal file
21
internal/generate/components/cue/configmap/configmap.cue
Normal file
@@ -0,0 +1,21 @@
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
|
||||
let Objects = {
|
||||
Name: "{{ .Name }}"
|
||||
Namespace: "{{ .Namespace }}"
|
||||
|
||||
Resources: {
|
||||
ConfigMap: {
|
||||
example: {
|
||||
metadata: namespace: "{{ .Namespace }}"
|
||||
// _Platform.Model represents the web form model
|
||||
data: platform: yaml.Marshal({model: _Platform.Model})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Produce a kubernetes objects build plan.
|
||||
(#Kubernetes & Objects).Output
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "configmap",
|
||||
"namespace": "default",
|
||||
"short": "simple configmap example",
|
||||
"long": "End-to-end demonstration of data flowing from the web ui to a cluster resource"
|
||||
}
|
||||
11
internal/generate/components/cue/namespaces/namespaces.cue
Normal file
11
internal/generate/components/cue/namespaces/namespaces.cue
Normal file
@@ -0,0 +1,11 @@
|
||||
package holos
|
||||
|
||||
let Objects = {
|
||||
Name: "{{ .Name }}"
|
||||
Namespace: "{{ .Namespace }}"
|
||||
|
||||
Resources: Namespace: _Namespaces
|
||||
}
|
||||
|
||||
// Produce a kubernetes objects build plan.
|
||||
(#Kubernetes & Objects).Output
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "namespaces",
|
||||
"namespace": "default",
|
||||
"short": "manage namespaces on multiple clusters",
|
||||
"long": "Manage namespaces across all clusters in the platform following sig-multicluster namespace sameness position."
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package holos
|
||||
|
||||
let Chart = {
|
||||
Name: "{{ .Name }}"
|
||||
Version: "{{ .Version }}"
|
||||
Namespace: "{{ .Namespace }}"
|
||||
|
||||
Repo: name: "{{ .RepoName }}"
|
||||
Repo: url: "{{ .RepoURL }}"
|
||||
|
||||
Values: {
|
||||
installCRDs: true
|
||||
startupapicheck: enabled: false
|
||||
// Must not use kube-system on gke autopilot. GKE Warden blocks access.
|
||||
global: leaderElection: namespace: Namespace
|
||||
}
|
||||
}
|
||||
|
||||
// Produce a helm chart build plan.
|
||||
(#Helm & Chart).Output
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "cert-manager",
|
||||
"short": "cloud native certificate management",
|
||||
"long": "Automatically provision and manage TLS certificates in Kubernetes",
|
||||
"chart": "cert-manager",
|
||||
"version": "1.14.5",
|
||||
"namespace": "cert-manager",
|
||||
"reponame": "jetstack",
|
||||
"repourl": "https://charts.jetstack.io"
|
||||
}
|
||||
15
internal/generate/components/helm/podinfo-oci/podinfo.cue
Normal file
15
internal/generate/components/helm/podinfo-oci/podinfo.cue
Normal file
@@ -0,0 +1,15 @@
|
||||
package holos
|
||||
|
||||
let Chart = {
|
||||
Name: "{{ .Name }}"
|
||||
Version: "{{ .Version }}"
|
||||
Namespace: "{{ .Namespace }}"
|
||||
|
||||
// OCI helm charts use the image url as the chart name
|
||||
Chart: chart: name: "{{ .Chart }}"
|
||||
|
||||
Values: {}
|
||||
}
|
||||
|
||||
// Produce a helm chart build plan.
|
||||
(#Helm & Chart).Output
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "podinfo-oci",
|
||||
"short": "oci helm chart example",
|
||||
"long": "Podinfo is a tiny web application made with Go that showcases best practices of running microservices in Kubernetes.",
|
||||
"chart": "oci://ghcr.io/stefanprodan/charts/podinfo",
|
||||
"version": "6.6.2",
|
||||
"namespace": "default"
|
||||
}
|
||||
15
internal/generate/components/helm/podinfo/podinfo.cue
Normal file
15
internal/generate/components/helm/podinfo/podinfo.cue
Normal file
@@ -0,0 +1,15 @@
|
||||
package holos
|
||||
|
||||
let Chart = {
|
||||
Name: "{{ .Name }}"
|
||||
Version: "{{ .Version }}"
|
||||
Namespace: "{{ .Namespace }}"
|
||||
|
||||
Repo: name: "{{ .RepoName }}"
|
||||
Repo: url: "{{ .RepoURL }}"
|
||||
|
||||
Values: {}
|
||||
}
|
||||
|
||||
// Produce a helm chart build plan.
|
||||
(#Helm & Chart).Output
|
||||
10
internal/generate/components/helm/podinfo/schematic.json
Normal file
10
internal/generate/components/helm/podinfo/schematic.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "podinfo",
|
||||
"short": "simple helm chart example",
|
||||
"long": "Podinfo is a tiny web application made with Go that showcases best practices of running microservices in Kubernetes.",
|
||||
"chart": "podinfo",
|
||||
"reponame": "podinfo",
|
||||
"repourl": "https://stefanprodan.github.io/podinfo",
|
||||
"version": "6.6.2",
|
||||
"namespace": "default"
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -12,49 +12,6 @@ import (
|
||||
"github.com/holos-run/holos/internal/server/middleware/logger"
|
||||
)
|
||||
|
||||
//go:embed all:platforms
|
||||
var platforms embed.FS
|
||||
|
||||
// root is the root path to copy platform cue code from.
|
||||
const root = "platforms"
|
||||
|
||||
// Platforms returns a slice of embedded platforms or nil if there are none.
|
||||
func Platforms() []string {
|
||||
entries, err := fs.ReadDir(platforms, root)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dirs := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && entry.Name() != "cue.mod" {
|
||||
dirs = append(dirs, entry.Name())
|
||||
}
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// GeneratePlatform writes the cue code for a platform to the local working
|
||||
// directory.
|
||||
func GeneratePlatform(ctx context.Context, name string) error {
|
||||
// Check for a valid platform
|
||||
platformPath := filepath.Join(root, name)
|
||||
if !dirExists(platforms, platformPath) {
|
||||
return errors.Wrap(fmt.Errorf("cannot generate: have: [%s] want: %+v", name, Platforms()))
|
||||
}
|
||||
|
||||
// Copy the cue.mod directory
|
||||
if err := copyEmbedFS(ctx, platforms, filepath.Join(root, "cue.mod"), "cue.mod"); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Copy the named platform
|
||||
if err := copyEmbedFS(ctx, platforms, platformPath, "."); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func dirExists(srcFS embed.FS, path string) bool {
|
||||
entries, err := fs.ReadDir(srcFS, path)
|
||||
if err != nil {
|
||||
@@ -63,7 +20,9 @@ func dirExists(srcFS embed.FS, path string) bool {
|
||||
return len(entries) > 0
|
||||
}
|
||||
|
||||
func copyEmbedFS(ctx context.Context, srcFS embed.FS, srcPath, dstPath string) error {
|
||||
// copyEmbedFS copies embedded files from srcPath to dstPath passing the
|
||||
// contents through mapFunc.
|
||||
func copyEmbedFS(ctx context.Context, srcFS embed.FS, srcPath, dstPath string, mapFunc func([]byte) *bytes.Buffer) error {
|
||||
log := logger.FromContext(ctx)
|
||||
return fs.WalkDir(srcFS, srcPath, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
@@ -82,16 +41,39 @@ func copyEmbedFS(ctx context.Context, srcFS embed.FS, srcPath, dstPath string) e
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.DebugContext(ctx, "created", "directory", dstFullPath)
|
||||
} else {
|
||||
data, err := srcFS.ReadFile(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if err := os.WriteFile(dstFullPath, data, os.ModePerm); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
log.DebugContext(ctx, "wrote", "file", dstFullPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
if filepath.Base(path) == "schematic.json" {
|
||||
log.DebugContext(ctx, "skipped", "file", dstFullPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
data, err := srcFS.ReadFile(path)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
buf := mapFunc(data)
|
||||
if err := os.WriteFile(dstFullPath, buf.Bytes(), 0666); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
log.DebugContext(ctx, "wrote", "file", dstFullPath)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func getCwd(ctx context.Context) string {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WarnContext(ctx, "could not get working directory", "err", err)
|
||||
return "."
|
||||
}
|
||||
abs, err := filepath.Abs(cwd)
|
||||
if err != nil {
|
||||
logger.FromContext(ctx).WarnContext(ctx, "could not get absolute path", "err", err)
|
||||
return cwd
|
||||
}
|
||||
return abs
|
||||
}
|
||||
|
||||
94
internal/generate/platform.go
Normal file
94
internal/generate/platform.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package generate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/holos-run/holos/internal/client"
|
||||
"github.com/holos-run/holos/internal/errors"
|
||||
"github.com/holos-run/holos/internal/logger"
|
||||
platform "github.com/holos-run/holos/service/gen/holos/platform/v1alpha1"
|
||||
)
|
||||
|
||||
//go:embed all:platforms
|
||||
var platforms embed.FS
|
||||
|
||||
// platformsRoot is the root path to copy platform cue code from.
|
||||
const platformsRoot = "platforms"
|
||||
|
||||
// Platforms returns a slice of embedded platforms or nil if there are none.
|
||||
func Platforms() []string {
|
||||
entries, err := fs.ReadDir(platforms, platformsRoot)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
dirs := make([]string, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() && entry.Name() != "cue.mod" {
|
||||
dirs = append(dirs, entry.Name())
|
||||
}
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// GeneratePlatform writes the cue code for a platform to the local working
|
||||
// directory.
|
||||
func GeneratePlatform(ctx context.Context, rpc *client.Client, orgID string, name string) error {
|
||||
log := logger.FromContext(ctx)
|
||||
// Check for a valid platform
|
||||
platformPath := filepath.Join(platformsRoot, name)
|
||||
if !dirExists(platforms, platformPath) {
|
||||
return errors.Wrap(fmt.Errorf("cannot generate: have: [%s] want: %+v", name, Platforms()))
|
||||
}
|
||||
|
||||
// Link the local platform the SaaS platform ID.
|
||||
rpcPlatforms, err := rpc.Platforms(ctx, orgID)
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
var rpcPlatform *platform.Platform
|
||||
for _, p := range rpcPlatforms {
|
||||
if p.GetName() == name {
|
||||
rpcPlatform = p
|
||||
break
|
||||
}
|
||||
}
|
||||
if rpcPlatform == nil {
|
||||
return errors.Wrap(errors.New("cannot generate: platform not found in the holos server"))
|
||||
}
|
||||
|
||||
// Write the platform data.
|
||||
data, err := json.MarshalIndent(rpcPlatform, "", " ")
|
||||
if err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
if len(data) > 0 {
|
||||
data = append(data, '\n')
|
||||
}
|
||||
log = log.With("platform_id", rpcPlatform.GetId())
|
||||
if err := os.WriteFile(client.PlatformMetadataFile, data, 0644); err != nil {
|
||||
return errors.Wrap(fmt.Errorf("could not write platform metadata: %w", err))
|
||||
}
|
||||
log.InfoContext(ctx, "wrote "+client.PlatformMetadataFile, "path", filepath.Join(getCwd(ctx), client.PlatformMetadataFile))
|
||||
|
||||
// Copy the cue.mod directory
|
||||
if err := copyEmbedFS(ctx, platforms, filepath.Join(platformsRoot, "cue.mod"), "cue.mod", bytes.NewBuffer); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
// Copy the named platform
|
||||
if err := copyEmbedFS(ctx, platforms, platformPath, ".", bytes.NewBuffer); err != nil {
|
||||
return errors.Wrap(err)
|
||||
}
|
||||
|
||||
log.InfoContext(ctx, "generated platform "+name, "path", getCwd(ctx))
|
||||
|
||||
return nil
|
||||
}
|
||||
34
internal/generate/platforms/bare/buildplan.cue
Normal file
34
internal/generate/platforms/bare/buildplan.cue
Normal file
@@ -0,0 +1,34 @@
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
// #Helm represents a holos build plan composed of one or more helm charts.
|
||||
#Helm: {
|
||||
Name: string
|
||||
Version: string
|
||||
Namespace: string
|
||||
|
||||
Repo: {
|
||||
name: string | *""
|
||||
url: string | *""
|
||||
}
|
||||
|
||||
Values: {...}
|
||||
|
||||
Chart: v1.#HelmChart & {
|
||||
metadata: name: string | *Name
|
||||
namespace: string | *Namespace
|
||||
chart: name: string | *Name
|
||||
chart: version: string | *Version
|
||||
chart: repository: Repo
|
||||
// Render the values to yaml for holos to provide to helm.
|
||||
valuesContent: yaml.Marshal(Values)
|
||||
}
|
||||
|
||||
// output represents the build plan provided to the holos cli.
|
||||
Output: v1.#BuildPlan & {
|
||||
spec: components: helmChartList: [Chart]
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
package holos
|
||||
|
||||
import "encoding/yaml"
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
let PLATFORM = {message: "TODO: Load the platform from the API."}
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
// Provide a BuildPlan to the holos cli to render k8s api objects.
|
||||
v1.#BuildPlan & {
|
||||
@@ -20,6 +19,12 @@ let OBJECTS = v1.#APIObjects & {
|
||||
name: "platform"
|
||||
namespace: "default"
|
||||
}
|
||||
// Output the platform model which is derived from the web app form the
|
||||
// platform engineer provides and the form values the end user provides.
|
||||
data: platform: yaml.Marshal(PLATFORM)
|
||||
}
|
||||
}
|
||||
|
||||
let PLATFORM = {
|
||||
spec: model: _Platform.spec.model
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package forms
|
||||
|
||||
import v1 "github.com/holos-run/holos/v1alpha1"
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
// Provides a concrete v1.#Form
|
||||
FormBuilder.Output
|
||||
@@ -24,22 +24,12 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
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."
|
||||
pattern: "It must be \(props.minLength) to \(props.maxLength) lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
minLength: "Must be at least \(props.minLength) characters"
|
||||
maxLength: "Must be at most \(props.maxLength) characters"
|
||||
}
|
||||
}
|
||||
|
||||
// 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.spec.config.user.sections.org.fields.displayName
|
||||
displayName: {
|
||||
type: "input"
|
||||
@@ -51,16 +41,6 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
required: true
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,13 +58,13 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
multiple: true
|
||||
selectAllOption: "Select All"
|
||||
options: [
|
||||
{value: "aws", label: "Amazon Web Services"},
|
||||
{value: "gcp", label: "Google Cloud Platform"},
|
||||
{value: "azure", label: "Microsoft Azure"},
|
||||
{value: "aws", label: "Amazon Web Services"},
|
||||
{value: "gcp", label: "Google Cloud Platform"},
|
||||
{value: "azure", label: "Microsoft Azure"},
|
||||
{value: "cloudflare", label: "Cloudflare"},
|
||||
{value: "github", label: "GitHub"},
|
||||
{value: "ois", label: "Open Infrastructure Services"},
|
||||
{value: "onprem", label: "On Premises", disabled: true},
|
||||
{value: "github", label: "GitHub"},
|
||||
{value: "ois", label: "Open Infrastructure Services"},
|
||||
{value: "onprem", label: "On Premises", disabled: true},
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -160,7 +140,9 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
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."
|
||||
pattern: "It must be \(props.minLength) to \(props.maxLength) lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
|
||||
minLength: "Must be at least \(props.minLength) characters."
|
||||
maxLength: "Must be at most \(props.maxLength) characters."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,102 +234,76 @@ let FormBuilder = v1.#FormBuilder & {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let GCPRegions = [
|
||||
{value: "africa-south1", label: "africa-south1"},
|
||||
{value: "asia-east1", label: "asia-east1"},
|
||||
{value: "asia-east2", label: "asia-east2"},
|
||||
{value: "asia-northeast1", label: "asia-northeast1"},
|
||||
{value: "asia-northeast2", label: "asia-northeast2"},
|
||||
{value: "asia-northeast3", label: "asia-northeast3"},
|
||||
{value: "asia-south1", label: "asia-south1"},
|
||||
{value: "asia-south2", label: "asia-south2"},
|
||||
{value: "asia-southeast1", label: "asia-southeast1"},
|
||||
{value: "asia-southeast2", label: "asia-southeast2"},
|
||||
{value: "australia-southeast1", label: "australia-southeast1"},
|
||||
{value: "australia-southeast2", label: "australia-southeast2"},
|
||||
{value: "europe-central2", label: "europe-central2"},
|
||||
{value: "europe-north1", label: "europe-north1"},
|
||||
{value: "europe-southwest1", label: "europe-southwest1"},
|
||||
{value: "europe-west1", label: "europe-west1"},
|
||||
{value: "europe-west10", label: "europe-west10"},
|
||||
{value: "europe-west12", label: "europe-west12"},
|
||||
{value: "europe-west2", label: "europe-west2"},
|
||||
{value: "europe-west3", label: "europe-west3"},
|
||||
{value: "europe-west4", label: "europe-west4"},
|
||||
{value: "europe-west6", label: "europe-west6"},
|
||||
{value: "europe-west8", label: "europe-west8"},
|
||||
{value: "europe-west9", label: "europe-west9"},
|
||||
{value: "me-central1", label: "me-central1"},
|
||||
{value: "me-central2", label: "me-central2"},
|
||||
{value: "me-west1", label: "me-west1"},
|
||||
{value: "africa-south1", label: "africa-south1"},
|
||||
{value: "asia-east1", label: "asia-east1"},
|
||||
{value: "asia-east2", label: "asia-east2"},
|
||||
{value: "asia-northeast1", label: "asia-northeast1"},
|
||||
{value: "asia-northeast2", label: "asia-northeast2"},
|
||||
{value: "asia-northeast3", label: "asia-northeast3"},
|
||||
{value: "asia-south1", label: "asia-south1"},
|
||||
{value: "asia-south2", label: "asia-south2"},
|
||||
{value: "asia-southeast1", label: "asia-southeast1"},
|
||||
{value: "asia-southeast2", label: "asia-southeast2"},
|
||||
{value: "australia-southeast1", label: "australia-southeast1"},
|
||||
{value: "australia-southeast2", label: "australia-southeast2"},
|
||||
{value: "europe-central2", label: "europe-central2"},
|
||||
{value: "europe-north1", label: "europe-north1"},
|
||||
{value: "europe-southwest1", label: "europe-southwest1"},
|
||||
{value: "europe-west1", label: "europe-west1"},
|
||||
{value: "europe-west10", label: "europe-west10"},
|
||||
{value: "europe-west12", label: "europe-west12"},
|
||||
{value: "europe-west2", label: "europe-west2"},
|
||||
{value: "europe-west3", label: "europe-west3"},
|
||||
{value: "europe-west4", label: "europe-west4"},
|
||||
{value: "europe-west6", label: "europe-west6"},
|
||||
{value: "europe-west8", label: "europe-west8"},
|
||||
{value: "europe-west9", label: "europe-west9"},
|
||||
{value: "me-central1", label: "me-central1"},
|
||||
{value: "me-central2", label: "me-central2"},
|
||||
{value: "me-west1", label: "me-west1"},
|
||||
{value: "northamerica-northeast1", label: "northamerica-northeast1"},
|
||||
{value: "northamerica-northeast2", label: "northamerica-northeast2"},
|
||||
{value: "southamerica-east1", label: "southamerica-east1"},
|
||||
{value: "southamerica-west1", label: "southamerica-west1"},
|
||||
{value: "us-central1", label: "us-central1"},
|
||||
{value: "us-east1", label: "us-east1"},
|
||||
{value: "us-east4", label: "us-east4"},
|
||||
{value: "us-east5", label: "us-east5"},
|
||||
{value: "us-south1", label: "us-south1"},
|
||||
{value: "us-west1", label: "us-west1"},
|
||||
{value: "us-west2", label: "us-west2"},
|
||||
{value: "us-west3", label: "us-west3"},
|
||||
{value: "us-west4", label: "us-west4"},
|
||||
{value: "southamerica-east1", label: "southamerica-east1"},
|
||||
{value: "southamerica-west1", label: "southamerica-west1"},
|
||||
{value: "us-central1", label: "us-central1"},
|
||||
{value: "us-east1", label: "us-east1"},
|
||||
{value: "us-east4", label: "us-east4"},
|
||||
{value: "us-east5", label: "us-east5"},
|
||||
{value: "us-south1", label: "us-south1"},
|
||||
{value: "us-west1", label: "us-west1"},
|
||||
{value: "us-west2", label: "us-west2"},
|
||||
{value: "us-west3", label: "us-west3"},
|
||||
{value: "us-west4", label: "us-west4"},
|
||||
]
|
||||
|
||||
let AWSRegions = [
|
||||
{value: "us-east-1", label: "N. Virginia (us-east-1)"},
|
||||
{value: "us-east-2", label: "Ohio (us-east-2)"},
|
||||
{value: "us-west-1", label: "N. California (us-west-1)"},
|
||||
{value: "us-west-2", label: "Oregon (us-west-2)"},
|
||||
{value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"},
|
||||
{value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"},
|
||||
{value: "ca-central-1", label: "Canada (ca-central-1)"},
|
||||
{value: "eu-north-1", label: "Stockholm (eu-north-1)"},
|
||||
{value: "eu-west-1", label: "Ireland (eu-west-1)"},
|
||||
{value: "eu-west-2", label: "London (eu-west-2)"},
|
||||
{value: "eu-west-3", label: "Paris (eu-west-3)"},
|
||||
{value: "eu-central-1", label: "Frankfurt (eu-central-1)"},
|
||||
{value: "eu-south-1", label: "Milan (eu-south-1)"},
|
||||
{value: "af-south-1", label: "Cape Town (af-south-1)"},
|
||||
{value: "us-east-1", label: "N. Virginia (us-east-1)"},
|
||||
{value: "us-east-2", label: "Ohio (us-east-2)"},
|
||||
{value: "us-west-1", label: "N. California (us-west-1)"},
|
||||
{value: "us-west-2", label: "Oregon (us-west-2)"},
|
||||
{value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"},
|
||||
{value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"},
|
||||
{value: "ca-central-1", label: "Canada (ca-central-1)"},
|
||||
{value: "eu-north-1", label: "Stockholm (eu-north-1)"},
|
||||
{value: "eu-west-1", label: "Ireland (eu-west-1)"},
|
||||
{value: "eu-west-2", label: "London (eu-west-2)"},
|
||||
{value: "eu-west-3", label: "Paris (eu-west-3)"},
|
||||
{value: "eu-central-1", label: "Frankfurt (eu-central-1)"},
|
||||
{value: "eu-south-1", label: "Milan (eu-south-1)"},
|
||||
{value: "af-south-1", label: "Cape Town (af-south-1)"},
|
||||
{value: "ap-northeast-1", label: "Tokyo (ap-northeast-1)"},
|
||||
{value: "ap-northeast-2", label: "Seoul (ap-northeast-2)"},
|
||||
{value: "ap-northeast-3", label: "Osaka (ap-northeast-3)"},
|
||||
{value: "ap-southeast-1", label: "Singapore (ap-southeast-1)"},
|
||||
{value: "ap-southeast-2", label: "Sydney (ap-southeast-2)"},
|
||||
{value: "ap-east-1", label: "Hong Kong (ap-east-1)"},
|
||||
{value: "ap-south-1", label: "Mumbai (ap-south-1)"},
|
||||
{value: "me-south-1", label: "Bahrain (me-south-1)"},
|
||||
{value: "sa-east-1", label: "São Paulo (sa-east-1)"},
|
||||
{value: "cn-north-1", label: "Bejing (cn-north-1)"},
|
||||
{value: "ap-east-1", label: "Hong Kong (ap-east-1)"},
|
||||
{value: "ap-south-1", label: "Mumbai (ap-south-1)"},
|
||||
{value: "me-south-1", label: "Bahrain (me-south-1)"},
|
||||
{value: "sa-east-1", label: "São Paulo (sa-east-1)"},
|
||||
{value: "cn-north-1", label: "Bejing (cn-north-1)"},
|
||||
{value: "cn-northwest-1", label: "Ningxia (cn-northwest-1)"},
|
||||
{value: "ap-southeast-3", label: "Jakarta (ap-southeast-3)"},
|
||||
]
|
||||
|
||||
47
internal/generate/platforms/bare/platform.cue
Normal file
47
internal/generate/platforms/bare/platform.cue
Normal file
@@ -0,0 +1,47 @@
|
||||
package holos
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
import v1 "github.com/holos-run/holos/api/v1alpha1"
|
||||
|
||||
import dto "github.com/holos-run/holos/service/gen/holos/object/v1alpha1:object"
|
||||
|
||||
// _PlatformConfig represents all of the data passed from holos to cue.
|
||||
// Intended to carry the platform model and project models.
|
||||
_PlatformConfig: dto.#PlatformConfig & json.Unmarshal(_PlatformConfigJSON)
|
||||
_PlatformConfigJSON: string | *"{}" @tag(platform_config, type=string)
|
||||
|
||||
// _Platform provides a platform resource to the holos cli for rendering. The
|
||||
// field is hidden because most components need to refer to platform data,
|
||||
// specifically the platform model and the project models. The platform
|
||||
// resource itself is output once when rendering the entire platform, see the
|
||||
// platform/ subdirectory.
|
||||
_Platform: v1.#Platform & {
|
||||
metadata: {
|
||||
name: string | *"bare" @tag(platform_name, type=string)
|
||||
}
|
||||
|
||||
// spec is the platform specification
|
||||
spec: {
|
||||
// model represents the web form values provided by the user.
|
||||
model: _PlatformConfig.platform_model
|
||||
components: [for c in _components {c}]
|
||||
|
||||
_components: [string]: v1.#PlatformSpecComponent
|
||||
_components: {
|
||||
for WorkloadCluster in _Clusters.Workload {
|
||||
"\(WorkloadCluster)-configmap": {
|
||||
path: "components/configmap"
|
||||
cluster: WorkloadCluster
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// _Clusters represents the clusters in the platform. The default values are
|
||||
// intended to be provided by the user in a file which is not written over by
|
||||
// `holos generate`.
|
||||
_Clusters: {
|
||||
Workload: [...string] | *["mycluster"]
|
||||
}
|
||||
4
internal/generate/platforms/bare/platform/platform.cue
Normal file
4
internal/generate/platforms/bare/platform/platform.cue
Normal file
@@ -0,0 +1,4 @@
|
||||
package holos
|
||||
|
||||
// Output the Platform resource for holos to render the entire platform.
|
||||
{} & _Platform
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,189 @@
|
||||
// Code generated by timoni. DO NOT EDIT.
|
||||
|
||||
//timoni:generate timoni vendor crd -f deploy/clusters/aws2/components/argocd-crds/argocd-crds.gen.yaml
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import "strings"
|
||||
|
||||
// AppProject provides a logical grouping of applications,
|
||||
// providing controls for: * where the apps may deploy to
|
||||
// (cluster whitelist) * what may be deployed (repository
|
||||
// whitelist, resource whitelist/blacklist) * who can access
|
||||
// these applications (roles, OIDC group claims bindings) * and
|
||||
// what they can do (RBAC policies) * automation access to these
|
||||
// roles (JWT tokens)
|
||||
#AppProject: {
|
||||
// APIVersion defines the versioned schema of this representation
|
||||
// of an object. Servers should convert recognized schemas to the
|
||||
// latest internal value, and may reject unrecognized values.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
apiVersion: "argoproj.io/v1alpha1"
|
||||
|
||||
// Kind is a string value representing the REST resource this
|
||||
// object represents. Servers may infer this from the endpoint
|
||||
// the client submits requests to. Cannot be updated. In
|
||||
// CamelCase. More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
kind: "AppProject"
|
||||
metadata: {
|
||||
name!: strings.MaxRunes(253) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
namespace!: strings.MaxRunes(63) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// AppProjectSpec is the specification of an AppProject
|
||||
spec!: #AppProjectSpec
|
||||
}
|
||||
|
||||
// AppProjectSpec is the specification of an AppProject
|
||||
#AppProjectSpec: {
|
||||
// ClusterResourceBlacklist contains list of blacklisted cluster
|
||||
// level resources
|
||||
clusterResourceBlacklist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// ClusterResourceWhitelist contains list of whitelisted cluster
|
||||
// level resources
|
||||
clusterResourceWhitelist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// Description contains optional project description
|
||||
description?: string
|
||||
|
||||
// Destinations contains list of destinations available for
|
||||
// deployment
|
||||
destinations?: [...{
|
||||
// Name is an alternate way of specifying the target cluster by
|
||||
// its symbolic name. This must be set if Server is not set.
|
||||
name?: string
|
||||
|
||||
// Namespace specifies the target namespace for the application's
|
||||
// resources. The namespace will only be set for namespace-scoped
|
||||
// resources that have not set a value for .metadata.namespace
|
||||
namespace?: string
|
||||
|
||||
// Server specifies the URL of the target cluster's Kubernetes
|
||||
// control plane API. This must be set if Name is not set.
|
||||
server?: string
|
||||
}]
|
||||
|
||||
// NamespaceResourceBlacklist contains list of blacklisted
|
||||
// namespace level resources
|
||||
namespaceResourceBlacklist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// NamespaceResourceWhitelist contains list of whitelisted
|
||||
// namespace level resources
|
||||
namespaceResourceWhitelist?: [...{
|
||||
group: string
|
||||
kind: string
|
||||
}]
|
||||
|
||||
// OrphanedResources specifies if controller should monitor
|
||||
// orphaned resources of apps in this project
|
||||
orphanedResources?: {
|
||||
// Ignore contains a list of resources that are to be excluded
|
||||
// from orphaned resources monitoring
|
||||
ignore?: [...{
|
||||
group?: string
|
||||
kind?: string
|
||||
name?: string
|
||||
}]
|
||||
|
||||
// Warn indicates if warning condition should be created for apps
|
||||
// which have orphaned resources
|
||||
warn?: bool
|
||||
}
|
||||
|
||||
// PermitOnlyProjectScopedClusters determines whether destinations
|
||||
// can only reference clusters which are project-scoped
|
||||
permitOnlyProjectScopedClusters?: bool
|
||||
|
||||
// Roles are user defined RBAC roles associated with this project
|
||||
roles?: [...{
|
||||
// Description is a description of the role
|
||||
description?: string
|
||||
|
||||
// Groups are a list of OIDC group claims bound to this role
|
||||
groups?: [...string]
|
||||
|
||||
// JWTTokens are a list of generated JWT tokens bound to this role
|
||||
jwtTokens?: [...{
|
||||
exp?: int
|
||||
iat: int
|
||||
id?: string
|
||||
}]
|
||||
|
||||
// Name is a name for this role
|
||||
name: string
|
||||
|
||||
// Policies Stores a list of casbin formatted strings that define
|
||||
// access policies for the role in the project
|
||||
policies?: [...string]
|
||||
}]
|
||||
|
||||
// SignatureKeys contains a list of PGP key IDs that commits in
|
||||
// Git must be signed with in order to be allowed for sync
|
||||
signatureKeys?: [...{
|
||||
// The ID of the key in hexadecimal notation
|
||||
keyID: string
|
||||
}]
|
||||
|
||||
// SourceNamespaces defines the namespaces application resources
|
||||
// are allowed to be created in
|
||||
sourceNamespaces?: [...string]
|
||||
|
||||
// SourceRepos contains list of repository URLs which can be used
|
||||
// for deployment
|
||||
sourceRepos?: [...string]
|
||||
|
||||
// SyncWindows controls when syncs can be run for apps in this
|
||||
// project
|
||||
syncWindows?: [...{
|
||||
// Applications contains a list of applications that the window
|
||||
// will apply to
|
||||
applications?: [...string]
|
||||
|
||||
// Clusters contains a list of clusters that the window will apply
|
||||
// to
|
||||
clusters?: [...string]
|
||||
|
||||
// Duration is the amount of time the sync window will be open
|
||||
duration?: string
|
||||
|
||||
// Kind defines if the window allows or blocks syncs
|
||||
kind?: string
|
||||
|
||||
// ManualSync enables manual syncs when they would otherwise be
|
||||
// blocked
|
||||
manualSync?: bool
|
||||
|
||||
// Namespaces contains a list of namespaces that the window will
|
||||
// apply to
|
||||
namespaces?: [...string]
|
||||
|
||||
// Schedule is the time the window will begin, specified in cron
|
||||
// format
|
||||
schedule?: string
|
||||
|
||||
// TimeZone of the sync that will be applied to the schedule
|
||||
timeZone?: string
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
// Code generated by timoni. DO NOT EDIT.
|
||||
|
||||
//timoni:generate timoni vendor crd -f https://raw.githubusercontent.com/crossplane-contrib/provider-upjet-aws/v1.5.0/package/crds/aws.upbound.io_providerconfigs.yaml
|
||||
|
||||
package v1beta1
|
||||
|
||||
import "strings"
|
||||
|
||||
// A ProviderConfig configures the AWS provider.
|
||||
#ProviderConfig: {
|
||||
// APIVersion defines the versioned schema of this representation
|
||||
// of an object.
|
||||
// Servers should convert recognized schemas to the latest
|
||||
// internal value, and
|
||||
// may reject unrecognized values.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
apiVersion: "aws.upbound.io/v1beta1"
|
||||
|
||||
// Kind is a string value representing the REST resource this
|
||||
// object represents.
|
||||
// Servers may infer this from the endpoint the client submits
|
||||
// requests to.
|
||||
// Cannot be updated.
|
||||
// In CamelCase.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
kind: "ProviderConfig"
|
||||
metadata!: {
|
||||
name!: strings.MaxRunes(253) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
namespace?: strings.MaxRunes(63) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// A ProviderConfigSpec defines the desired state of a
|
||||
// ProviderConfig.
|
||||
spec!: #ProviderConfigSpec
|
||||
}
|
||||
|
||||
// A ProviderConfigSpec defines the desired state of a
|
||||
// ProviderConfig.
|
||||
#ProviderConfigSpec: {
|
||||
// AssumeRoleChain defines the options for assuming an IAM role
|
||||
assumeRoleChain?: [...{
|
||||
// ExternalID is the external ID used when assuming role.
|
||||
externalID?: string
|
||||
|
||||
// AssumeRoleARN to assume with provider credentials
|
||||
roleARN?: string
|
||||
|
||||
// Tags is list of session tags that you want to pass. Each
|
||||
// session tag consists of a key
|
||||
// name and an associated value. For more information about
|
||||
// session tags, see
|
||||
// Tagging STS Sessions
|
||||
// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html).
|
||||
tags?: [...{
|
||||
// Name of the tag.
|
||||
// Key is a required field
|
||||
key: string
|
||||
|
||||
// Value of the tag.
|
||||
// Value is a required field
|
||||
value: string
|
||||
}]
|
||||
|
||||
// TransitiveTagKeys is a list of keys for session tags that you
|
||||
// want to set as transitive. If you set a
|
||||
// tag key as transitive, the corresponding key and value passes
|
||||
// to subsequent
|
||||
// sessions in a role chain. For more information, see Chaining
|
||||
// Roles with Session Tags
|
||||
// (https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html#id_session-tags_role-chaining).
|
||||
transitiveTagKeys?: [...string]
|
||||
}]
|
||||
|
||||
// Credentials required to authenticate to this provider.
|
||||
credentials: {
|
||||
env?: {
|
||||
// Name is the name of an environment variable.
|
||||
name: string
|
||||
}
|
||||
fs?: {
|
||||
// Path is a filesystem path.
|
||||
path: string
|
||||
}
|
||||
|
||||
// A SecretRef is a reference to a secret key that contains the
|
||||
// credentials
|
||||
// that must be used to connect to the provider.
|
||||
secretRef?: {
|
||||
// The key to select.
|
||||
key: string
|
||||
|
||||
// Name of the secret.
|
||||
name: string
|
||||
|
||||
// Namespace of the secret.
|
||||
namespace: string
|
||||
}
|
||||
|
||||
// Source of the provider credentials.
|
||||
source: "None" | "Secret" | "IRSA" | "WebIdentity" | "Upbound"
|
||||
upbound?: {
|
||||
// WebIdentity defines the options for assuming an IAM role with a
|
||||
// Web
|
||||
// Identity.
|
||||
webIdentity?: {
|
||||
// AssumeRoleARN to assume with provider credentials
|
||||
roleARN?: string
|
||||
|
||||
// RoleSessionName is the session name, if you wish to uniquely
|
||||
// identify this session.
|
||||
roleSessionName?: string
|
||||
|
||||
// TokenConfig is the Web Identity Token config to assume the
|
||||
// role.
|
||||
tokenConfig?: {
|
||||
fs?: {
|
||||
// Path is a filesystem path.
|
||||
path: string
|
||||
}
|
||||
|
||||
// A SecretRef is a reference to a secret key that contains the
|
||||
// credentials
|
||||
// that must be used to obtain the web identity token.
|
||||
secretRef?: {
|
||||
// The key to select.
|
||||
key: string
|
||||
|
||||
// Name of the secret.
|
||||
name: string
|
||||
|
||||
// Namespace of the secret.
|
||||
namespace: string
|
||||
}
|
||||
|
||||
// Source is the source of the web identity token.
|
||||
source: "Secret" | "Filesystem"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WebIdentity defines the options for assuming an IAM role with a
|
||||
// Web Identity.
|
||||
webIdentity?: {
|
||||
// AssumeRoleARN to assume with provider credentials
|
||||
roleARN?: string
|
||||
|
||||
// RoleSessionName is the session name, if you wish to uniquely
|
||||
// identify this session.
|
||||
roleSessionName?: string
|
||||
|
||||
// TokenConfig is the Web Identity Token config to assume the
|
||||
// role.
|
||||
tokenConfig?: {
|
||||
fs?: {
|
||||
// Path is a filesystem path.
|
||||
path: string
|
||||
}
|
||||
|
||||
// A SecretRef is a reference to a secret key that contains the
|
||||
// credentials
|
||||
// that must be used to obtain the web identity token.
|
||||
secretRef?: {
|
||||
// The key to select.
|
||||
key: string
|
||||
|
||||
// Name of the secret.
|
||||
name: string
|
||||
|
||||
// Namespace of the secret.
|
||||
namespace: string
|
||||
}
|
||||
|
||||
// Source is the source of the web identity token.
|
||||
source: "Secret" | "Filesystem"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Endpoint is where you can override the default endpoint
|
||||
// configuration
|
||||
// of AWS calls made by the provider.
|
||||
endpoint?: {
|
||||
// Specifies if the endpoint's hostname can be modified by the
|
||||
// SDK's API
|
||||
// client.
|
||||
//
|
||||
//
|
||||
// If the hostname is mutable the SDK API clients may modify any
|
||||
// part of
|
||||
// the hostname based on the requirements of the API, (e.g.
|
||||
// adding, or
|
||||
// removing content in the hostname). Such as, Amazon S3 API
|
||||
// client
|
||||
// prefixing "bucketname" to the hostname, or changing the
|
||||
// hostname service name component from "s3." to
|
||||
// "s3-accesspoint.dualstack."
|
||||
// for the dualstack endpoint of an S3 Accesspoint resource.
|
||||
//
|
||||
//
|
||||
// Care should be taken when providing a custom endpoint for an
|
||||
// API. If the
|
||||
// endpoint hostname is mutable, and the client cannot modify the
|
||||
// endpoint
|
||||
// correctly, the operation call will most likely fail, or have
|
||||
// undefined
|
||||
// behavior.
|
||||
//
|
||||
//
|
||||
// If hostname is immutable, the SDK API clients will not modify
|
||||
// the
|
||||
// hostname of the URL. This may cause the API client not to
|
||||
// function
|
||||
// correctly if the API requires the operation specific hostname
|
||||
// values
|
||||
// to be used by the client.
|
||||
//
|
||||
//
|
||||
// This flag does not modify the API client's behavior if this
|
||||
// endpoint
|
||||
// will be used instead of Endpoint Discovery, or if the endpoint
|
||||
// will be
|
||||
// used to perform Endpoint Discovery. That behavior is configured
|
||||
// via the
|
||||
// API Client's Options.
|
||||
// Note that this is effective only for resources that use AWS SDK
|
||||
// v2.
|
||||
hostnameImmutable?: bool
|
||||
|
||||
// The AWS partition the endpoint belongs to.
|
||||
partitionId?: string
|
||||
|
||||
// Specifies the list of services you want endpoint to be used for
|
||||
services?: [...string]
|
||||
|
||||
// The signing method that should be used for signing the requests
|
||||
// to the
|
||||
// endpoint.
|
||||
signingMethod?: string
|
||||
|
||||
// The service name that should be used for signing the requests
|
||||
// to the
|
||||
// endpoint.
|
||||
signingName?: string
|
||||
|
||||
// The region that should be used for signing the request to the
|
||||
// endpoint.
|
||||
// For IAM, which doesn't have any region, us-east-1 is used to
|
||||
// sign the
|
||||
// requests, which is the only signing region of IAM.
|
||||
signingRegion?: string
|
||||
|
||||
// The source of the Endpoint. By default, this will be
|
||||
// ServiceMetadata.
|
||||
// When providing a custom endpoint, you should set the source as
|
||||
// Custom.
|
||||
// If source is not provided when providing a custom endpoint, the
|
||||
// SDK may not
|
||||
// perform required host mutations correctly. Source should be
|
||||
// used along with
|
||||
// HostnameImmutable property as per the usage requirement.
|
||||
// Note that this is effective only for resources that use AWS SDK
|
||||
// v2.
|
||||
source?: "ServiceMetadata" | "Custom"
|
||||
|
||||
// URL lets you configure the endpoint URL to be used in SDK
|
||||
// calls.
|
||||
url: {
|
||||
// Dynamic lets you configure the behavior of endpoint URL
|
||||
// resolver.
|
||||
dynamic?: {
|
||||
// Host is the address of the main host that the resolver will use
|
||||
// to
|
||||
// prepend protocol, service and region configurations.
|
||||
// For example, the final URL for EC2 in us-east-1 looks like
|
||||
// https://ec2.us-east-1.amazonaws.com
|
||||
// You would need to use "amazonaws.com" as Host and "https" as
|
||||
// protocol
|
||||
// to have the resolver construct it.
|
||||
host: string
|
||||
|
||||
// Protocol is the HTTP protocol that will be used in the URL.
|
||||
// Currently,
|
||||
// only http and https are supported.
|
||||
protocol: "http" | "https"
|
||||
}
|
||||
|
||||
// Static is the full URL you'd like the AWS SDK to use.
|
||||
// Recommended for using tools like localstack where a single host
|
||||
// is exposed
|
||||
// for all services and regions.
|
||||
static?: string
|
||||
|
||||
// You can provide a static URL that will be used regardless of
|
||||
// the service
|
||||
// and region by choosing Static type. Alternatively, you can
|
||||
// provide
|
||||
// configuration for dynamically resolving the URL with the config
|
||||
// you provide
|
||||
// once you set the type as Dynamic.
|
||||
type: "Static" | "Dynamic"
|
||||
}
|
||||
}
|
||||
|
||||
// Whether to enable the request to use path-style addressing,
|
||||
// i.e., https://s3.amazonaws.com/BUCKET/KEY.
|
||||
s3_use_path_style?: bool
|
||||
|
||||
// Whether to skip credentials validation via the STS API.
|
||||
// This can be useful for testing and for AWS API implementations
|
||||
// that do not have STS available.
|
||||
skip_credentials_validation?: bool
|
||||
|
||||
// Whether to skip the AWS Metadata API check
|
||||
// Useful for AWS API implementations that do not have a metadata
|
||||
// API endpoint.
|
||||
skip_metadata_api_check?: bool
|
||||
|
||||
// Whether to skip validation of provided region name.
|
||||
// Useful for AWS-like implementations that use their own region
|
||||
// names or to bypass the validation for
|
||||
// regions that aren't publicly available yet.
|
||||
skip_region_validation?: bool
|
||||
|
||||
// Whether to skip requesting the account ID.
|
||||
// Useful for AWS API implementations that do not have the IAM,
|
||||
// STS API, or metadata API
|
||||
skip_requesting_account_id?: bool
|
||||
}
|
||||
@@ -0,0 +1,422 @@
|
||||
// Code generated by timoni. DO NOT EDIT.
|
||||
|
||||
//timoni:generate timoni vendor crd -f /home/jeff/workspace/holos-run/holos-infra/deploy/clusters/k2/components/prod-mesh-certmanager/prod-mesh-certmanager.gen.yaml
|
||||
|
||||
package v1
|
||||
|
||||
import "strings"
|
||||
|
||||
// A Certificate resource should be created to ensure an up to
|
||||
// date and signed X.509 certificate is stored in the Kubernetes
|
||||
// Secret resource named in `spec.secretName`.
|
||||
// The stored certificate will be renewed before it expires (as
|
||||
// configured by `spec.renewBefore`).
|
||||
#Certificate: {
|
||||
// APIVersion defines the versioned schema of this representation
|
||||
// of an object. Servers should convert recognized schemas to the
|
||||
// latest internal value, and may reject unrecognized values.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
apiVersion: "cert-manager.io/v1"
|
||||
|
||||
// Kind is a string value representing the REST resource this
|
||||
// object represents. Servers may infer this from the endpoint
|
||||
// the client submits requests to. Cannot be updated. In
|
||||
// CamelCase. More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
kind: "Certificate"
|
||||
metadata!: {
|
||||
name!: strings.MaxRunes(253) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
namespace!: strings.MaxRunes(63) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// Specification of the desired state of the Certificate resource.
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
spec!: #CertificateSpec
|
||||
}
|
||||
|
||||
// Specification of the desired state of the Certificate resource.
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
#CertificateSpec: {
|
||||
// Defines extra output formats of the private key and signed
|
||||
// certificate chain to be written to this Certificate's target
|
||||
// Secret.
|
||||
// This is an Alpha Feature and is only enabled with the
|
||||
// `--feature-gates=AdditionalCertificateOutputFormats=true`
|
||||
// option set on both the controller and webhook components.
|
||||
additionalOutputFormats?: [...{
|
||||
// Type is the name of the format type that should be written to
|
||||
// the Certificate's target Secret.
|
||||
type: "DER" | "CombinedPEM"
|
||||
}]
|
||||
|
||||
// Requested common name X509 certificate subject attribute. More
|
||||
// info:
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6
|
||||
// NOTE: TLS clients will ignore this value when any subject
|
||||
// alternative name is set (see
|
||||
// https://tools.ietf.org/html/rfc6125#section-6.4.4).
|
||||
// Should have a length of 64 characters or fewer to avoid
|
||||
// generating invalid CSRs. Cannot be set if the `literalSubject`
|
||||
// field is set.
|
||||
commonName?: string
|
||||
|
||||
// Requested DNS subject alternative names.
|
||||
dnsNames?: [...string]
|
||||
|
||||
// Requested 'duration' (i.e. lifetime) of the Certificate. Note
|
||||
// that the issuer may choose to ignore the requested duration,
|
||||
// just like any other requested attribute.
|
||||
// If unset, this defaults to 90 days. Minimum accepted duration
|
||||
// is 1 hour. Value must be in units accepted by Go
|
||||
// time.ParseDuration https://golang.org/pkg/time/#ParseDuration.
|
||||
duration?: string
|
||||
|
||||
// Requested email subject alternative names.
|
||||
emailAddresses?: [...string]
|
||||
|
||||
// Whether the KeyUsage and ExtKeyUsage extensions should be set
|
||||
// in the encoded CSR.
|
||||
// This option defaults to true, and should only be disabled if
|
||||
// the target issuer does not support CSRs with these X509
|
||||
// KeyUsage/ ExtKeyUsage extensions.
|
||||
encodeUsagesInRequest?: bool
|
||||
|
||||
// Requested IP address subject alternative names.
|
||||
ipAddresses?: [...string]
|
||||
|
||||
// Requested basic constraints isCA value. The isCA value is used
|
||||
// to set the `isCA` field on the created CertificateRequest
|
||||
// resources. Note that the issuer may choose to ignore the
|
||||
// requested isCA value, just like any other requested attribute.
|
||||
// If true, this will automatically add the `cert sign` usage to
|
||||
// the list of requested `usages`.
|
||||
isCA?: bool
|
||||
|
||||
// Reference to the issuer responsible for issuing the
|
||||
// certificate. If the issuer is namespace-scoped, it must be in
|
||||
// the same namespace as the Certificate. If the issuer is
|
||||
// cluster-scoped, it can be used from any namespace.
|
||||
// The `name` field of the reference must always be specified.
|
||||
issuerRef: {
|
||||
// Group of the resource being referred to.
|
||||
group?: string
|
||||
|
||||
// Kind of the resource being referred to.
|
||||
kind?: string
|
||||
|
||||
// Name of the resource being referred to.
|
||||
name: string
|
||||
}
|
||||
|
||||
// Additional keystore output formats to be stored in the
|
||||
// Certificate's Secret.
|
||||
keystores?: {
|
||||
// JKS configures options for storing a JKS keystore in the
|
||||
// `spec.secretName` Secret resource.
|
||||
jks?: {
|
||||
// Create enables JKS keystore creation for the Certificate. If
|
||||
// true, a file named `keystore.jks` will be created in the
|
||||
// target Secret resource, encrypted using the password stored in
|
||||
// `passwordSecretRef`. The keystore file will be updated
|
||||
// immediately. If the issuer provided a CA certificate, a file
|
||||
// named `truststore.jks` will also be created in the target
|
||||
// Secret resource, encrypted using the password stored in
|
||||
// `passwordSecretRef` containing the issuing Certificate
|
||||
// Authority
|
||||
create: bool
|
||||
|
||||
// PasswordSecretRef is a reference to a key in a Secret resource
|
||||
// containing the password used to encrypt the JKS keystore.
|
||||
passwordSecretRef: {
|
||||
// The key of the entry in the Secret resource's `data` field to
|
||||
// be used. Some instances of this field may be defaulted, in
|
||||
// others it may be required.
|
||||
key?: string
|
||||
|
||||
// Name of the resource being referred to. More info:
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
// PKCS12 configures options for storing a PKCS12 keystore in the
|
||||
// `spec.secretName` Secret resource.
|
||||
pkcs12?: {
|
||||
// Create enables PKCS12 keystore creation for the Certificate. If
|
||||
// true, a file named `keystore.p12` will be created in the
|
||||
// target Secret resource, encrypted using the password stored in
|
||||
// `passwordSecretRef`. The keystore file will be updated
|
||||
// immediately. If the issuer provided a CA certificate, a file
|
||||
// named `truststore.p12` will also be created in the target
|
||||
// Secret resource, encrypted using the password stored in
|
||||
// `passwordSecretRef` containing the issuing Certificate
|
||||
// Authority
|
||||
create: bool
|
||||
|
||||
// PasswordSecretRef is a reference to a key in a Secret resource
|
||||
// containing the password used to encrypt the PKCS12 keystore.
|
||||
passwordSecretRef: {
|
||||
// The key of the entry in the Secret resource's `data` field to
|
||||
// be used. Some instances of this field may be defaulted, in
|
||||
// others it may be required.
|
||||
key?: string
|
||||
|
||||
// Name of the resource being referred to. More info:
|
||||
// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
name: string
|
||||
}
|
||||
|
||||
// Profile specifies the key and certificate encryption algorithms
|
||||
// and the HMAC algorithm used to create the PKCS12 keystore.
|
||||
// Default value is `LegacyRC2` for backward compatibility.
|
||||
// If provided, allowed values are: `LegacyRC2`: Deprecated. Not
|
||||
// supported by default in OpenSSL 3 or Java 20. `LegacyDES`:
|
||||
// Less secure algorithm. Use this option for maximal
|
||||
// compatibility. `Modern2023`: Secure algorithm. Use this option
|
||||
// in case you have to always use secure algorithms (eg. because
|
||||
// of company policy). Please note that the security of the
|
||||
// algorithm is not that important in reality, because the
|
||||
// unencrypted certificate and private key are also stored in the
|
||||
// Secret.
|
||||
profile?: "LegacyRC2" | "LegacyDES" | "Modern2023"
|
||||
}
|
||||
}
|
||||
|
||||
// Requested X.509 certificate subject, represented using the LDAP
|
||||
// "String Representation of a Distinguished Name" [1].
|
||||
// Important: the LDAP string format also specifies the order of
|
||||
// the attributes in the subject, this is important when issuing
|
||||
// certs for LDAP authentication. Example:
|
||||
// `CN=foo,DC=corp,DC=example,DC=com` More info [1]:
|
||||
// https://datatracker.ietf.org/doc/html/rfc4514 More info:
|
||||
// https://github.com/cert-manager/cert-manager/issues/3203 More
|
||||
// info: https://github.com/cert-manager/cert-manager/issues/4424
|
||||
// Cannot be set if the `subject` or `commonName` field is set.
|
||||
// This is an Alpha Feature and is only enabled with the
|
||||
// `--feature-gates=LiteralCertificateSubject=true` option set on
|
||||
// both the controller and webhook components.
|
||||
literalSubject?: string
|
||||
|
||||
// x.509 certificate NameConstraint extension which MUST NOT be
|
||||
// used in a non-CA certificate. More Info:
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.10
|
||||
// This is an Alpha Feature and is only enabled with the
|
||||
// `--feature-gates=NameConstraints=true` option set on both the
|
||||
// controller and webhook components.
|
||||
nameConstraints?: {
|
||||
// if true then the name constraints are marked critical.
|
||||
critical?: bool
|
||||
|
||||
// Excluded contains the constraints which must be disallowed. Any
|
||||
// name matching a restriction in the excluded field is invalid
|
||||
// regardless of information appearing in the permitted
|
||||
excluded?: {
|
||||
// DNSDomains is a list of DNS domains that are permitted or
|
||||
// excluded.
|
||||
dnsDomains?: [...string]
|
||||
|
||||
// EmailAddresses is a list of Email Addresses that are permitted
|
||||
// or excluded.
|
||||
emailAddresses?: [...string]
|
||||
|
||||
// IPRanges is a list of IP Ranges that are permitted or excluded.
|
||||
// This should be a valid CIDR notation.
|
||||
ipRanges?: [...string]
|
||||
|
||||
// URIDomains is a list of URI domains that are permitted or
|
||||
// excluded.
|
||||
uriDomains?: [...string]
|
||||
}
|
||||
|
||||
// Permitted contains the constraints in which the names must be
|
||||
// located.
|
||||
permitted?: {
|
||||
// DNSDomains is a list of DNS domains that are permitted or
|
||||
// excluded.
|
||||
dnsDomains?: [...string]
|
||||
|
||||
// EmailAddresses is a list of Email Addresses that are permitted
|
||||
// or excluded.
|
||||
emailAddresses?: [...string]
|
||||
|
||||
// IPRanges is a list of IP Ranges that are permitted or excluded.
|
||||
// This should be a valid CIDR notation.
|
||||
ipRanges?: [...string]
|
||||
|
||||
// URIDomains is a list of URI domains that are permitted or
|
||||
// excluded.
|
||||
uriDomains?: [...string]
|
||||
}
|
||||
}
|
||||
|
||||
// `otherNames` is an escape hatch for SAN that allows any type.
|
||||
// We currently restrict the support to string like otherNames,
|
||||
// cf RFC 5280 p 37 Any UTF8 String valued otherName can be
|
||||
// passed with by setting the keys oid: x.x.x.x and UTF8Value:
|
||||
// somevalue for `otherName`. Most commonly this would be UPN set
|
||||
// with oid: 1.3.6.1.4.1.311.20.2.3 You should ensure that any
|
||||
// OID passed is valid for the UTF8String type as we do not
|
||||
// explicitly validate this.
|
||||
otherNames?: [...{
|
||||
// OID is the object identifier for the otherName SAN. The object
|
||||
// identifier must be expressed as a dotted string, for example,
|
||||
// "1.2.840.113556.1.4.221".
|
||||
oid?: string
|
||||
|
||||
// utf8Value is the string value of the otherName SAN. The
|
||||
// utf8Value accepts any valid UTF8 string to set as value for
|
||||
// the otherName SAN.
|
||||
utf8Value?: string
|
||||
}]
|
||||
|
||||
// Private key options. These include the key algorithm and size,
|
||||
// the used encoding and the rotation policy.
|
||||
privateKey?: {
|
||||
// Algorithm is the private key algorithm of the corresponding
|
||||
// private key for this certificate.
|
||||
// If provided, allowed values are either `RSA`, `ECDSA` or
|
||||
// `Ed25519`. If `algorithm` is specified and `size` is not
|
||||
// provided, key size of 2048 will be used for `RSA` key
|
||||
// algorithm and key size of 256 will be used for `ECDSA` key
|
||||
// algorithm. key size is ignored when using the `Ed25519` key
|
||||
// algorithm.
|
||||
algorithm?: "RSA" | "ECDSA" | "Ed25519"
|
||||
|
||||
// The private key cryptography standards (PKCS) encoding for this
|
||||
// certificate's private key to be encoded in.
|
||||
// If provided, allowed values are `PKCS1` and `PKCS8` standing
|
||||
// for PKCS#1 and PKCS#8, respectively. Defaults to `PKCS1` if
|
||||
// not specified.
|
||||
encoding?: "PKCS1" | "PKCS8"
|
||||
|
||||
// RotationPolicy controls how private keys should be regenerated
|
||||
// when a re-issuance is being processed.
|
||||
// If set to `Never`, a private key will only be generated if one
|
||||
// does not already exist in the target `spec.secretName`. If one
|
||||
// does exists but it does not have the correct algorithm or
|
||||
// size, a warning will be raised to await user intervention. If
|
||||
// set to `Always`, a private key matching the specified
|
||||
// requirements will be generated whenever a re-issuance occurs.
|
||||
// Default is `Never` for backward compatibility.
|
||||
rotationPolicy?: "Never" | "Always"
|
||||
|
||||
// Size is the key bit size of the corresponding private key for
|
||||
// this certificate.
|
||||
// If `algorithm` is set to `RSA`, valid values are `2048`, `4096`
|
||||
// or `8192`, and will default to `2048` if not specified. If
|
||||
// `algorithm` is set to `ECDSA`, valid values are `256`, `384`
|
||||
// or `521`, and will default to `256` if not specified. If
|
||||
// `algorithm` is set to `Ed25519`, Size is ignored. No other
|
||||
// values are allowed.
|
||||
size?: int
|
||||
}
|
||||
|
||||
// How long before the currently issued certificate's expiry
|
||||
// cert-manager should renew the certificate. For example, if a
|
||||
// certificate is valid for 60 minutes, and `renewBefore=10m`,
|
||||
// cert-manager will begin to attempt to renew the certificate 50
|
||||
// minutes after it was issued (i.e. when there are 10 minutes
|
||||
// remaining until the certificate is no longer valid).
|
||||
// NOTE: The actual lifetime of the issued certificate is used to
|
||||
// determine the renewal time. If an issuer returns a certificate
|
||||
// with a different lifetime than the one requested, cert-manager
|
||||
// will use the lifetime of the issued certificate.
|
||||
// If unset, this defaults to 1/3 of the issued certificate's
|
||||
// lifetime. Minimum accepted value is 5 minutes. Value must be
|
||||
// in units accepted by Go time.ParseDuration
|
||||
// https://golang.org/pkg/time/#ParseDuration.
|
||||
renewBefore?: string
|
||||
|
||||
// The maximum number of CertificateRequest revisions that are
|
||||
// maintained in the Certificate's history. Each revision
|
||||
// represents a single `CertificateRequest` created by this
|
||||
// Certificate, either when it was created, renewed, or Spec was
|
||||
// changed. Revisions will be removed by oldest first if the
|
||||
// number of revisions exceeds this number.
|
||||
// If set, revisionHistoryLimit must be a value of `1` or greater.
|
||||
// If unset (`nil`), revisions will not be garbage collected.
|
||||
// Default value is `nil`.
|
||||
revisionHistoryLimit?: int
|
||||
|
||||
// Name of the Secret resource that will be automatically created
|
||||
// and managed by this Certificate resource. It will be populated
|
||||
// with a private key and certificate, signed by the denoted
|
||||
// issuer. The Secret resource lives in the same namespace as the
|
||||
// Certificate resource.
|
||||
secretName: string
|
||||
|
||||
// Defines annotations and labels to be copied to the
|
||||
// Certificate's Secret. Labels and annotations on the Secret
|
||||
// will be changed as they appear on the SecretTemplate when
|
||||
// added or removed. SecretTemplate annotations are added in
|
||||
// conjunction with, and cannot overwrite, the base set of
|
||||
// annotations cert-manager sets on the Certificate's Secret.
|
||||
secretTemplate?: {
|
||||
// Annotations is a key value map to be copied to the target
|
||||
// Kubernetes Secret.
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
|
||||
// Labels is a key value map to be copied to the target Kubernetes
|
||||
// Secret.
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// Requested set of X509 certificate subject attributes. More
|
||||
// info:
|
||||
// https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6
|
||||
// The common name attribute is specified separately in the
|
||||
// `commonName` field. Cannot be set if the `literalSubject`
|
||||
// field is set.
|
||||
subject?: {
|
||||
// Countries to be used on the Certificate.
|
||||
countries?: [...string]
|
||||
|
||||
// Cities to be used on the Certificate.
|
||||
localities?: [...string]
|
||||
|
||||
// Organizational Units to be used on the Certificate.
|
||||
organizationalUnits?: [...string]
|
||||
|
||||
// Organizations to be used on the Certificate.
|
||||
organizations?: [...string]
|
||||
|
||||
// Postal codes to be used on the Certificate.
|
||||
postalCodes?: [...string]
|
||||
|
||||
// State/Provinces to be used on the Certificate.
|
||||
provinces?: [...string]
|
||||
|
||||
// Serial number to be used on the Certificate.
|
||||
serialNumber?: string
|
||||
|
||||
// Street addresses to be used on the Certificate.
|
||||
streetAddresses?: [...string]
|
||||
}
|
||||
|
||||
// Requested URI subject alternative names.
|
||||
uris?: [...string]
|
||||
|
||||
// Requested key usages and extended key usages. These usages are
|
||||
// used to set the `usages` field on the created
|
||||
// CertificateRequest resources. If `encodeUsagesInRequest` is
|
||||
// unset or set to `true`, the usages will additionally be
|
||||
// encoded in the `request` field which contains the CSR blob.
|
||||
// If unset, defaults to `digital signature` and `key
|
||||
// encipherment`.
|
||||
usages?: [..."signing" | "digital signature" | "content commitment" | "key encipherment" | "key agreement" | "data encipherment" | "cert sign" | "crl sign" | "encipher only" | "decipher only" | "any" | "server auth" | "client auth" | "code signing" | "email protection" | "s/mime" | "ipsec end system" | "ipsec tunnel" | "ipsec user" | "timestamping" | "ocsp signing" | "microsoft sgc" | "netscape sgc"]
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
// Code generated by timoni. DO NOT EDIT.
|
||||
|
||||
//timoni:generate timoni vendor crd -f /home/jeff/workspace/holos-run/holos-infra/deploy/clusters/k2/components/prod-mesh-certmanager/prod-mesh-certmanager.gen.yaml
|
||||
|
||||
package v1
|
||||
|
||||
import "strings"
|
||||
|
||||
// A CertificateRequest is used to request a signed certificate
|
||||
// from one of the configured issuers.
|
||||
// All fields within the CertificateRequest's `spec` are immutable
|
||||
// after creation. A CertificateRequest will either succeed or
|
||||
// fail, as denoted by its `Ready` status condition and its
|
||||
// `status.failureTime` field.
|
||||
// A CertificateRequest is a one-shot resource, meaning it
|
||||
// represents a single point in time request for a certificate
|
||||
// and cannot be re-used.
|
||||
#CertificateRequest: {
|
||||
// APIVersion defines the versioned schema of this representation
|
||||
// of an object. Servers should convert recognized schemas to the
|
||||
// latest internal value, and may reject unrecognized values.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
apiVersion: "cert-manager.io/v1"
|
||||
|
||||
// Kind is a string value representing the REST resource this
|
||||
// object represents. Servers may infer this from the endpoint
|
||||
// the client submits requests to. Cannot be updated. In
|
||||
// CamelCase. More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
kind: "CertificateRequest"
|
||||
metadata!: {
|
||||
name!: strings.MaxRunes(253) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
namespace!: strings.MaxRunes(63) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// Specification of the desired state of the CertificateRequest
|
||||
// resource.
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
spec!: #CertificateRequestSpec
|
||||
}
|
||||
|
||||
// Specification of the desired state of the CertificateRequest
|
||||
// resource.
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
|
||||
#CertificateRequestSpec: {
|
||||
// Requested 'duration' (i.e. lifetime) of the Certificate. Note
|
||||
// that the issuer may choose to ignore the requested duration,
|
||||
// just like any other requested attribute.
|
||||
duration?: string
|
||||
|
||||
// Extra contains extra attributes of the user that created the
|
||||
// CertificateRequest. Populated by the cert-manager webhook on
|
||||
// creation and immutable.
|
||||
extra?: {
|
||||
[string]: [...string]
|
||||
}
|
||||
|
||||
// Groups contains group membership of the user that created the
|
||||
// CertificateRequest. Populated by the cert-manager webhook on
|
||||
// creation and immutable.
|
||||
groups?: [...string]
|
||||
|
||||
// Requested basic constraints isCA value. Note that the issuer
|
||||
// may choose to ignore the requested isCA value, just like any
|
||||
// other requested attribute.
|
||||
// NOTE: If the CSR in the `Request` field has a BasicConstraints
|
||||
// extension, it must have the same isCA value as specified here.
|
||||
// If true, this will automatically add the `cert sign` usage to
|
||||
// the list of requested `usages`.
|
||||
isCA?: bool
|
||||
|
||||
// Reference to the issuer responsible for issuing the
|
||||
// certificate. If the issuer is namespace-scoped, it must be in
|
||||
// the same namespace as the Certificate. If the issuer is
|
||||
// cluster-scoped, it can be used from any namespace.
|
||||
// The `name` field of the reference must always be specified.
|
||||
issuerRef: {
|
||||
// Group of the resource being referred to.
|
||||
group?: string
|
||||
|
||||
// Kind of the resource being referred to.
|
||||
kind?: string
|
||||
|
||||
// Name of the resource being referred to.
|
||||
name: string
|
||||
}
|
||||
|
||||
// The PEM-encoded X.509 certificate signing request to be
|
||||
// submitted to the issuer for signing.
|
||||
// If the CSR has a BasicConstraints extension, its isCA attribute
|
||||
// must match the `isCA` value of this CertificateRequest. If the
|
||||
// CSR has a KeyUsage extension, its key usages must match the
|
||||
// key usages in the `usages` field of this CertificateRequest.
|
||||
// If the CSR has a ExtKeyUsage extension, its extended key
|
||||
// usages must match the extended key usages in the `usages`
|
||||
// field of this CertificateRequest.
|
||||
request: string
|
||||
|
||||
// UID contains the uid of the user that created the
|
||||
// CertificateRequest. Populated by the cert-manager webhook on
|
||||
// creation and immutable.
|
||||
uid?: string
|
||||
|
||||
// Requested key usages and extended key usages.
|
||||
// NOTE: If the CSR in the `Request` field has uses the KeyUsage
|
||||
// or ExtKeyUsage extension, these extensions must have the same
|
||||
// values as specified here without any additional values.
|
||||
// If unset, defaults to `digital signature` and `key
|
||||
// encipherment`.
|
||||
usages?: [..."signing" | "digital signature" | "content commitment" | "key encipherment" | "key agreement" | "data encipherment" | "cert sign" | "crl sign" | "encipher only" | "decipher only" | "any" | "server auth" | "client auth" | "code signing" | "email protection" | "s/mime" | "ipsec end system" | "ipsec tunnel" | "ipsec user" | "timestamping" | "ocsp signing" | "microsoft sgc" | "netscape sgc"]
|
||||
|
||||
// Username contains the name of the user that created the
|
||||
// CertificateRequest. Populated by the cert-manager webhook on
|
||||
// creation and immutable.
|
||||
username?: string
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,148 @@
|
||||
// Code generated by timoni. DO NOT EDIT.
|
||||
|
||||
//timoni:generate timoni vendor crd -f deploy/clusters/aws2/components/istio-base/istio-base.gen.yaml
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"list"
|
||||
)
|
||||
|
||||
#WasmPlugin: {
|
||||
// Extend the functionality provided by the Istio proxy through
|
||||
// WebAssembly filters. See more details at:
|
||||
// https://istio.io/docs/reference/config/proxy_extensions/wasm-plugin.html
|
||||
spec!: #WasmPluginSpec
|
||||
apiVersion: "extensions.istio.io/v1alpha1"
|
||||
kind: "WasmPlugin"
|
||||
metadata!: {
|
||||
name!: strings.MaxRunes(253) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
namespace!: strings.MaxRunes(63) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extend the functionality provided by the Istio proxy through
|
||||
// WebAssembly filters. See more details at:
|
||||
// https://istio.io/docs/reference/config/proxy_extensions/wasm-plugin.html
|
||||
#WasmPluginSpec: {
|
||||
// Specifies the failure behavior for the plugin due to fatal
|
||||
// errors.
|
||||
//
|
||||
// Valid Options: FAIL_CLOSE, FAIL_OPEN
|
||||
failStrategy?: "FAIL_CLOSE" | "FAIL_OPEN"
|
||||
|
||||
// The pull behaviour to be applied when fetching Wasm module by
|
||||
// either OCI image or `http/https`.
|
||||
//
|
||||
// Valid Options: IfNotPresent, Always
|
||||
imagePullPolicy?: "UNSPECIFIED_POLICY" | "IfNotPresent" | "Always"
|
||||
|
||||
// Credentials to use for OCI image pulling.
|
||||
imagePullSecret?: strings.MaxRunes(253) & strings.MinRunes(1)
|
||||
|
||||
// Specifies the criteria to determine which traffic is passed to
|
||||
// WasmPlugin.
|
||||
match?: [...{
|
||||
// Criteria for selecting traffic by their direction.
|
||||
//
|
||||
// Valid Options: CLIENT, SERVER, CLIENT_AND_SERVER
|
||||
mode?: "UNDEFINED" | "CLIENT" | "SERVER" | "CLIENT_AND_SERVER"
|
||||
|
||||
// Criteria for selecting traffic by their destination port.
|
||||
ports?: [...{
|
||||
number: uint16 & >=1
|
||||
}]
|
||||
}]
|
||||
|
||||
// Determines where in the filter chain this `WasmPlugin` is to be
|
||||
// injected.
|
||||
//
|
||||
// Valid Options: AUTHN, AUTHZ, STATS
|
||||
phase?: "UNSPECIFIED_PHASE" | "AUTHN" | "AUTHZ" | "STATS"
|
||||
|
||||
// The configuration that will be passed on to the plugin.
|
||||
pluginConfig?: {
|
||||
...
|
||||
}
|
||||
|
||||
// The plugin name to be used in the Envoy configuration (used to
|
||||
// be called `rootID`).
|
||||
pluginName?: strings.MaxRunes(256) & strings.MinRunes(1)
|
||||
|
||||
// Determines ordering of `WasmPlugins` in the same `phase`.
|
||||
priority?: null | int
|
||||
selector?: {
|
||||
// One or more labels that indicate a specific set of pods/VMs on
|
||||
// which a policy should be applied.
|
||||
matchLabels?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// SHA256 checksum that will be used to verify Wasm module or OCI
|
||||
// container.
|
||||
sha256?: =~"(^$|^[a-f0-9]{64}$)"
|
||||
targetRef?: {
|
||||
// group is the group of the target resource.
|
||||
group?: string
|
||||
|
||||
// kind is kind of the target resource.
|
||||
kind?: string
|
||||
|
||||
// name is the name of the target resource.
|
||||
name?: string
|
||||
|
||||
// namespace is the namespace of the referent.
|
||||
namespace?: string
|
||||
}
|
||||
|
||||
// Optional.
|
||||
targetRefs?: [...{
|
||||
// group is the group of the target resource.
|
||||
group?: string
|
||||
|
||||
// kind is kind of the target resource.
|
||||
kind?: string
|
||||
|
||||
// name is the name of the target resource.
|
||||
name?: string
|
||||
|
||||
// namespace is the namespace of the referent.
|
||||
namespace?: string
|
||||
}]
|
||||
|
||||
// Specifies the type of Wasm Extension to be used.
|
||||
//
|
||||
// Valid Options: HTTP, NETWORK
|
||||
type?: "UNSPECIFIED_PLUGIN_TYPE" | "HTTP" | "NETWORK"
|
||||
|
||||
// URL of a Wasm module or OCI container.
|
||||
url: strings.MinRunes(1)
|
||||
verificationKey?: string
|
||||
vmConfig?: {
|
||||
// Specifies environment variables to be injected to this VM.
|
||||
env?: list.MaxItems(256) & [...{
|
||||
// Name of the environment variable.
|
||||
name: strings.MaxRunes(256) & strings.MinRunes(1)
|
||||
|
||||
// Value for the environment variable.
|
||||
value?: strings.MaxRunes(2048)
|
||||
|
||||
// Source for the environment variable's value.
|
||||
//
|
||||
// Valid Options: INLINE, HOST
|
||||
valueFrom?: "INLINE" | "HOST"
|
||||
}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,378 @@
|
||||
// Code generated by timoni. DO NOT EDIT.
|
||||
|
||||
//timoni:generate timoni vendor crd -f /home/jeff/workspace/holos-run/holos-infra/deploy/clusters/k2/components/prod-secrets-eso/prod-secrets-eso.gen.yaml
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"struct"
|
||||
)
|
||||
|
||||
// ClusterExternalSecret is the Schema for the
|
||||
// clusterexternalsecrets API.
|
||||
#ClusterExternalSecret: {
|
||||
// APIVersion defines the versioned schema of this representation
|
||||
// of an object.
|
||||
// Servers should convert recognized schemas to the latest
|
||||
// internal value, and
|
||||
// may reject unrecognized values.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
apiVersion: "external-secrets.io/v1beta1"
|
||||
|
||||
// Kind is a string value representing the REST resource this
|
||||
// object represents.
|
||||
// Servers may infer this from the endpoint the client submits
|
||||
// requests to.
|
||||
// Cannot be updated.
|
||||
// In CamelCase.
|
||||
// More info:
|
||||
// https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
kind: "ClusterExternalSecret"
|
||||
metadata!: {
|
||||
name!: strings.MaxRunes(253) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
namespace?: strings.MaxRunes(63) & strings.MinRunes(1) & {
|
||||
string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterExternalSecretSpec defines the desired state of
|
||||
// ClusterExternalSecret.
|
||||
spec!: #ClusterExternalSecretSpec
|
||||
}
|
||||
|
||||
// ClusterExternalSecretSpec defines the desired state of
|
||||
// ClusterExternalSecret.
|
||||
#ClusterExternalSecretSpec: {
|
||||
// The metadata of the external secrets to be created
|
||||
externalSecretMetadata?: {
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// The name of the external secrets to be created defaults to the
|
||||
// name of the ClusterExternalSecret
|
||||
externalSecretName?: string
|
||||
|
||||
// The spec for the ExternalSecrets to be created
|
||||
externalSecretSpec: {
|
||||
// Data defines the connection between the Kubernetes Secret keys
|
||||
// and the Provider data
|
||||
data?: [...{
|
||||
// RemoteRef points to the remote secret and defines
|
||||
// which secret (version/property/..) to fetch.
|
||||
remoteRef: {
|
||||
// Used to define a conversion Strategy
|
||||
conversionStrategy?: "Default" | "Unicode" | *"Default"
|
||||
|
||||
// Used to define a decoding Strategy
|
||||
decodingStrategy?: "Auto" | "Base64" | "Base64URL" | "None" | *"None"
|
||||
|
||||
// Key is the key used in the Provider, mandatory
|
||||
key: string
|
||||
|
||||
// Policy for fetching tags/labels from provider secrets, possible
|
||||
// options are Fetch, None. Defaults to None
|
||||
metadataPolicy?: "None" | "Fetch" | *"None"
|
||||
|
||||
// Used to select a specific property of the Provider value (if a
|
||||
// map), if supported
|
||||
property?: string
|
||||
|
||||
// Used to select a specific version of the Provider value, if
|
||||
// supported
|
||||
version?: string
|
||||
}
|
||||
|
||||
// SecretKey defines the key in which the controller stores
|
||||
// the value. This is the key in the Kind=Secret
|
||||
secretKey: string
|
||||
|
||||
// SourceRef allows you to override the source
|
||||
// from which the value will pulled from.
|
||||
sourceRef?: struct.MaxFields(1) & {
|
||||
// GeneratorRef points to a generator custom resource.
|
||||
//
|
||||
//
|
||||
// Deprecated: The generatorRef is not implemented in .data[].
|
||||
// this will be removed with v1.
|
||||
generatorRef?: {
|
||||
// Specify the apiVersion of the generator resource
|
||||
apiVersion?: string | *"generators.external-secrets.io/v1alpha1"
|
||||
|
||||
// Specify the Kind of the resource, e.g. Password, ACRAccessToken
|
||||
// etc.
|
||||
kind: string
|
||||
|
||||
// Specify the name of the generator resource
|
||||
name: string
|
||||
}
|
||||
|
||||
// SecretStoreRef defines which SecretStore to fetch the
|
||||
// ExternalSecret data.
|
||||
storeRef?: {
|
||||
// Kind of the SecretStore resource (SecretStore or
|
||||
// ClusterSecretStore)
|
||||
// Defaults to `SecretStore`
|
||||
kind?: string
|
||||
|
||||
// Name of the SecretStore resource
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// DataFrom is used to fetch all properties from a specific
|
||||
// Provider data
|
||||
// If multiple entries are specified, the Secret keys are merged
|
||||
// in the specified order
|
||||
dataFrom?: [...{
|
||||
// Used to extract multiple key/value pairs from one secret
|
||||
// Note: Extract does not support sourceRef.Generator or
|
||||
// sourceRef.GeneratorRef.
|
||||
extract?: {
|
||||
// Used to define a conversion Strategy
|
||||
conversionStrategy?: "Default" | "Unicode" | *"Default"
|
||||
|
||||
// Used to define a decoding Strategy
|
||||
decodingStrategy?: "Auto" | "Base64" | "Base64URL" | "None" | *"None"
|
||||
|
||||
// Key is the key used in the Provider, mandatory
|
||||
key: string
|
||||
|
||||
// Policy for fetching tags/labels from provider secrets, possible
|
||||
// options are Fetch, None. Defaults to None
|
||||
metadataPolicy?: "None" | "Fetch" | *"None"
|
||||
|
||||
// Used to select a specific property of the Provider value (if a
|
||||
// map), if supported
|
||||
property?: string
|
||||
|
||||
// Used to select a specific version of the Provider value, if
|
||||
// supported
|
||||
version?: string
|
||||
}
|
||||
|
||||
// Used to find secrets based on tags or regular expressions
|
||||
// Note: Find does not support sourceRef.Generator or
|
||||
// sourceRef.GeneratorRef.
|
||||
find?: {
|
||||
// Used to define a conversion Strategy
|
||||
conversionStrategy?: "Default" | "Unicode" | *"Default"
|
||||
|
||||
// Used to define a decoding Strategy
|
||||
decodingStrategy?: "Auto" | "Base64" | "Base64URL" | "None" | *"None"
|
||||
name?: {
|
||||
// Finds secrets base
|
||||
regexp?: string
|
||||
}
|
||||
|
||||
// A root path to start the find operations.
|
||||
path?: string
|
||||
|
||||
// Find secrets based on tags.
|
||||
tags?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// Used to rewrite secret Keys after getting them from the secret
|
||||
// Provider
|
||||
// Multiple Rewrite operations can be provided. They are applied
|
||||
// in a layered order (first to last)
|
||||
rewrite?: [...{
|
||||
// Used to rewrite with regular expressions.
|
||||
// The resulting key will be the output of a regexp.ReplaceAll
|
||||
// operation.
|
||||
regexp?: {
|
||||
// Used to define the regular expression of a re.Compiler.
|
||||
source: string
|
||||
|
||||
// Used to define the target pattern of a ReplaceAll operation.
|
||||
target: string
|
||||
}
|
||||
transform?: {
|
||||
// Used to define the template to apply on the secret name.
|
||||
// `.value ` will specify the secret name in the template.
|
||||
template: string
|
||||
}
|
||||
}]
|
||||
|
||||
// SourceRef points to a store or generator
|
||||
// which contains secret values ready to use.
|
||||
// Use this in combination with Extract or Find pull values out of
|
||||
// a specific SecretStore.
|
||||
// When sourceRef points to a generator Extract or Find is not
|
||||
// supported.
|
||||
// The generator returns a static map of values
|
||||
sourceRef?: struct.MaxFields(1) & {
|
||||
// GeneratorRef points to a generator custom resource.
|
||||
generatorRef?: {
|
||||
// Specify the apiVersion of the generator resource
|
||||
apiVersion?: string | *"generators.external-secrets.io/v1alpha1"
|
||||
|
||||
// Specify the Kind of the resource, e.g. Password, ACRAccessToken
|
||||
// etc.
|
||||
kind: string
|
||||
|
||||
// Specify the name of the generator resource
|
||||
name: string
|
||||
}
|
||||
|
||||
// SecretStoreRef defines which SecretStore to fetch the
|
||||
// ExternalSecret data.
|
||||
storeRef?: {
|
||||
// Kind of the SecretStore resource (SecretStore or
|
||||
// ClusterSecretStore)
|
||||
// Defaults to `SecretStore`
|
||||
kind?: string
|
||||
|
||||
// Name of the SecretStore resource
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}]
|
||||
|
||||
// RefreshInterval is the amount of time before the values are
|
||||
// read again from the SecretStore provider
|
||||
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
|
||||
// May be set to zero to fetch and create it once. Defaults to 1h.
|
||||
refreshInterval?: string | *"1h"
|
||||
|
||||
// SecretStoreRef defines which SecretStore to fetch the
|
||||
// ExternalSecret data.
|
||||
secretStoreRef?: {
|
||||
// Kind of the SecretStore resource (SecretStore or
|
||||
// ClusterSecretStore)
|
||||
// Defaults to `SecretStore`
|
||||
kind?: string
|
||||
|
||||
// Name of the SecretStore resource
|
||||
name: string
|
||||
}
|
||||
|
||||
// ExternalSecretTarget defines the Kubernetes Secret to be
|
||||
// created
|
||||
// There can be only one target per ExternalSecret.
|
||||
target?: {
|
||||
// CreationPolicy defines rules on how to create the resulting
|
||||
// Secret
|
||||
// Defaults to 'Owner'
|
||||
creationPolicy?: "Owner" | "Orphan" | "Merge" | "None" | *"Owner"
|
||||
|
||||
// DeletionPolicy defines rules on how to delete the resulting
|
||||
// Secret
|
||||
// Defaults to 'Retain'
|
||||
deletionPolicy?: "Delete" | "Merge" | "Retain" | *"Retain"
|
||||
|
||||
// Immutable defines if the final secret will be immutable
|
||||
immutable?: bool
|
||||
|
||||
// Name defines the name of the Secret resource to be managed
|
||||
// This field is immutable
|
||||
// Defaults to the .metadata.name of the ExternalSecret resource
|
||||
name?: string
|
||||
|
||||
// Template defines a blueprint for the created Secret resource.
|
||||
template?: {
|
||||
data?: {
|
||||
[string]: string
|
||||
}
|
||||
|
||||
// EngineVersion specifies the template engine version
|
||||
// that should be used to compile/execute the
|
||||
// template specified in .data and .templateFrom[].
|
||||
engineVersion?: "v1" | "v2" | *"v2"
|
||||
mergePolicy?: "Replace" | "Merge" | *"Replace"
|
||||
|
||||
// ExternalSecretTemplateMetadata defines metadata fields for the
|
||||
// Secret blueprint.
|
||||
metadata?: {
|
||||
annotations?: {
|
||||
[string]: string
|
||||
}
|
||||
labels?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
templateFrom?: [...{
|
||||
configMap?: {
|
||||
items: [...{
|
||||
key: string
|
||||
templateAs?: "Values" | "KeysAndValues" | *"Values"
|
||||
}]
|
||||
name: string
|
||||
}
|
||||
literal?: string
|
||||
secret?: {
|
||||
items: [...{
|
||||
key: string
|
||||
templateAs?: "Values" | "KeysAndValues" | *"Values"
|
||||
}]
|
||||
name: string
|
||||
}
|
||||
target?: "Data" | "Annotations" | "Labels" | *"Data"
|
||||
}]
|
||||
type?: string
|
||||
}
|
||||
} | *{
|
||||
creationPolicy: "Owner"
|
||||
deletionPolicy: "Retain"
|
||||
}
|
||||
}
|
||||
|
||||
// The labels to select by to find the Namespaces to create the
|
||||
// ExternalSecrets in.
|
||||
namespaceSelector?: {
|
||||
// matchExpressions is a list of label selector requirements. The
|
||||
// requirements are ANDed.
|
||||
matchExpressions?: [...{
|
||||
// key is the label key that the selector applies to.
|
||||
key: string
|
||||
|
||||
// operator represents a key's relationship to a set of values.
|
||||
// Valid operators are In, NotIn, Exists and DoesNotExist.
|
||||
operator: string
|
||||
|
||||
// values is an array of string values. If the operator is In or
|
||||
// NotIn,
|
||||
// the values array must be non-empty. If the operator is Exists
|
||||
// or DoesNotExist,
|
||||
// the values array must be empty. This array is replaced during a
|
||||
// strategic
|
||||
// merge patch.
|
||||
values?: [...string]
|
||||
}]
|
||||
|
||||
// matchLabels is a map of {key,value} pairs. A single {key,value}
|
||||
// in the matchLabels
|
||||
// map is equivalent to an element of matchExpressions, whose key
|
||||
// field is "key", the
|
||||
// operator is "In", and the values array contains only "value".
|
||||
// The requirements are ANDed.
|
||||
matchLabels?: {
|
||||
[string]: string
|
||||
}
|
||||
}
|
||||
|
||||
// Choose namespaces by name. This field is ORed with anything
|
||||
// that NamespaceSelector ends up choosing.
|
||||
namespaces?: [...string]
|
||||
|
||||
// The time in which the controller should reconcile its objects
|
||||
// and recheck namespaces for labels.
|
||||
refreshTime?: string
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user