Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e335541c6c | ||
|
|
3c1fcd9d6e | ||
|
|
4fca94d863 | ||
|
|
a3d49f0d6e | ||
|
|
f432a445a0 | ||
|
|
effaa9badf | ||
|
|
ac6be04859 | ||
|
|
c0ca7e7392 | ||
|
|
2f0b883724 | ||
|
|
7b8eed0347 | ||
|
|
230a2f18b8 | ||
|
|
89578d891f | ||
|
|
8995af06fa | ||
|
|
55752aee1c | ||
|
|
a90ba17904 | ||
|
|
6f78984561 | ||
|
|
b927caed96 | ||
|
|
e4e8a5e217 | ||
|
|
804bafd4e6 | ||
|
|
f2a9508aba | ||
|
|
392b9f711b | ||
|
|
2d9f35067f | ||
|
|
a0fd53deaa | ||
|
|
e346e10c07 | ||
|
|
f1dc54650e | ||
|
|
9ed5d588d0 | ||
|
|
6eb24faf63 | ||
|
|
daa13906b5 | ||
|
|
b2ce455aa2 | ||
|
|
90629b84b5 | ||
|
|
7f077f5347 | ||
|
|
327193215b | ||
|
|
b98b5cae3f | ||
|
|
ddba69517f | ||
|
|
e28642bca7 | ||
|
|
dceb37b7ab | ||
|
|
f6340ea4fe | ||
|
|
3845174738 | ||
|
|
f0bc21a606 | ||
|
|
6d0e48fccb | ||
|
|
f5035ce699 | ||
|
|
3c694d2a92 | ||
|
|
b8592b0b72 | ||
|
|
cf2289ef19 | ||
|
|
5e5b9c97d4 | ||
|
|
a19e0ff3f3 | ||
|
|
ac632cb407 | ||
|
|
154bbabf01 | ||
|
|
95e45d59cb | ||
|
|
a45abedd32 | ||
|
|
a644b1181b | ||
|
|
861b552b0b | ||
|
|
5d0212e832 | ||
|
|
9f434928d6 | ||
|
|
5b1fa4b046 | ||
|
|
ae4614c35b | ||
|
|
e99a00f0a1 | ||
|
|
e89dcb9783 | ||
|
|
05806cb439 | ||
|
|
bfb8458bcb | ||
|
|
55d4033116 | ||
|
|
276dc95029 | ||
|
|
c473321817 | ||
|
|
1b539b8874 | ||
|
|
1892fe31ae | ||
|
|
b9c1e49822 | ||
|
|
f31a630139 | ||
|
|
a4445c7d17 | ||
|
|
d0b392cfe0 | ||
|
|
efc215dc8c | ||
|
|
2f446bd60a | ||
|
|
92889cb9a4 | ||
|
|
c53e00f609 | ||
|
|
5e2c0e7d64 |
88
.cspell.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"version": "0.2",
|
||||
"language": "en",
|
||||
"enableFiletypes": [
|
||||
"mdx"
|
||||
],
|
||||
"words": [
|
||||
"applicationset",
|
||||
"argoproj",
|
||||
"authcode",
|
||||
"authpolicy",
|
||||
"authproxy",
|
||||
"authroutes",
|
||||
"buildplan",
|
||||
"cainjector",
|
||||
"clusterissuer",
|
||||
"cookiesecret",
|
||||
"coredns",
|
||||
"crds",
|
||||
"crossplane",
|
||||
"cuecontext",
|
||||
"cuelang",
|
||||
"devicecode",
|
||||
"dnsmasq",
|
||||
"dscacheutil",
|
||||
"entgo",
|
||||
"errgroup",
|
||||
"fieldmaskpb",
|
||||
"flushcache",
|
||||
"gitops",
|
||||
"grpcreflect",
|
||||
"grpcurl",
|
||||
"holos",
|
||||
"holoslogger",
|
||||
"httpbin",
|
||||
"Infima",
|
||||
"isatty",
|
||||
"istiod",
|
||||
"jetstack",
|
||||
"killall",
|
||||
"kubeadm",
|
||||
"kubeconfig",
|
||||
"kubelogin",
|
||||
"Kustomization",
|
||||
"kustomize",
|
||||
"ldflags",
|
||||
"libnss",
|
||||
"loadbalancer",
|
||||
"mattn",
|
||||
"mxcl",
|
||||
"myhostname",
|
||||
"nameserver",
|
||||
"organizationconnect",
|
||||
"orgid",
|
||||
"otelconnect",
|
||||
"Parentspanid",
|
||||
"pflag",
|
||||
"PKCE",
|
||||
"platformconnect",
|
||||
"promhttp",
|
||||
"protojson",
|
||||
"putenv",
|
||||
"quickstart",
|
||||
"retryable",
|
||||
"ropc",
|
||||
"spanid",
|
||||
"spiffe",
|
||||
"startupapicheck",
|
||||
"structpb",
|
||||
"systemconnect",
|
||||
"tablewriter",
|
||||
"Tiltfile",
|
||||
"timestamppb",
|
||||
"tlsclientconfig",
|
||||
"tokencache",
|
||||
"Tokener",
|
||||
"Traceid",
|
||||
"traefik",
|
||||
"uibutton",
|
||||
"Upsert",
|
||||
"urandom",
|
||||
"usecases",
|
||||
"userconnect",
|
||||
"userdata",
|
||||
"zerolog",
|
||||
"zitadel"
|
||||
]
|
||||
}
|
||||
@@ -41,10 +41,6 @@ jobs:
|
||||
run: |
|
||||
set -x
|
||||
make tools
|
||||
make buf
|
||||
go generate ./...
|
||||
make frontend
|
||||
go mod tidy
|
||||
|
||||
- name: Test
|
||||
run: ./scripts/test
|
||||
4
.github/workflows/lint.yaml
vendored
@@ -37,10 +37,6 @@ jobs:
|
||||
run: |
|
||||
set -x
|
||||
make tools
|
||||
make buf
|
||||
go generate ./...
|
||||
make frontend
|
||||
go mod tidy
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v4
|
||||
|
||||
4
.github/workflows/release.yaml
vendored
@@ -40,10 +40,6 @@ jobs:
|
||||
run: |
|
||||
set -x
|
||||
make tools
|
||||
make buf
|
||||
go generate ./...
|
||||
make frontend
|
||||
go mod tidy
|
||||
|
||||
- name: Import GPG key
|
||||
uses: crazy-max/ghaction-import-gpg@v6
|
||||
|
||||
6
.gitignore
vendored
@@ -7,3 +7,9 @@ coverage.out
|
||||
/deploy/
|
||||
.vscode/
|
||||
tmp/
|
||||
.DS_*
|
||||
|
||||
# In case we run through the tutorial in this directory.
|
||||
/holos-k3d/
|
||||
/holos-infra/
|
||||
node_modules/
|
||||
|
||||
13
.ko.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
# Refer to https://ko.build/configuration/#overriding-go-build-settings
|
||||
builds:
|
||||
- id: holos
|
||||
dir: .
|
||||
main: ./cmd/holos
|
||||
env:
|
||||
- GOPRIVATE=github.com/holos-run/\*
|
||||
ldflags:
|
||||
- -s
|
||||
- -w
|
||||
- -X
|
||||
# Makefile provides GIT_DETAIL and GIT_SUFFIX.
|
||||
- github.com/holos-run/holos/version.GitDescribe={{.Env.GIT_DETAIL}}{{.Env.GIT_SUFFIX}}
|
||||
34
Makefile
@@ -48,6 +48,10 @@ bumpmajor: ## Bump the major version.
|
||||
show-version: ## Print the full version.
|
||||
@echo $(VERSION)
|
||||
|
||||
.PHONY: tag
|
||||
tag: ## Tag a release
|
||||
git tag v$(VERSION)
|
||||
|
||||
.PHONY: tidy
|
||||
tidy: ## Tidy go module.
|
||||
go mod tidy
|
||||
@@ -72,6 +76,11 @@ build: ## Build holos executable.
|
||||
@echo "GOPATH=${GOPATH}"
|
||||
go build -trimpath -o bin/$(BIN_NAME) -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/$(BIN_NAME)
|
||||
|
||||
linux: ## Build holos executable for tilt.
|
||||
@echo "building ${BIN_NAME}.linux ${VERSION}"
|
||||
@echo "GOPATH=${GOPATH}"
|
||||
GOOS=linux go build -trimpath -o bin/$(BIN_NAME).linux -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/$(BIN_NAME)
|
||||
|
||||
.PHONY: install
|
||||
install: build ## Install holos to GOPATH/bin
|
||||
install bin/$(BIN_NAME) $(shell go env GOPATH)/bin/$(BIN_NAME)
|
||||
@@ -89,6 +98,7 @@ lint: ## Run linters.
|
||||
buf lint
|
||||
cd internal/frontend/holos && ng lint
|
||||
golangci-lint run
|
||||
./hack/cspell
|
||||
|
||||
.PHONY: coverage
|
||||
coverage: test ## Test coverage profile.
|
||||
@@ -110,25 +120,29 @@ go-deps: ## tool versions pinned in tools.go
|
||||
go install connectrpc.com/connect/cmd/protoc-gen-connect-go
|
||||
go install honnef.co/go/tools/cmd/staticcheck
|
||||
go install golang.org/x/tools/cmd/godoc
|
||||
go install github.com/princjef/gomarkdoc/cmd/gomarkdoc
|
||||
go install github.com/google/ko
|
||||
# curl https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash
|
||||
|
||||
.PHONY: frontend-deps
|
||||
frontend-deps: ## Install Angular deps for go generate
|
||||
cd internal/frontend/holos && npm install
|
||||
cd internal/frontend/holos && npm install --save-dev @bufbuild/buf @connectrpc/protoc-gen-connect-es
|
||||
cd internal/frontend/holos && npm install @connectrpc/connect @connectrpc/connect-web @bufbuild/protobuf
|
||||
# https://github.com/connectrpc/connect-query-es/blob/1350b6f07b6aead81793917954bdb1cc3ce09df9/packages/protoc-gen-connect-query/README.md?plain=1#L23
|
||||
cd internal/frontend/holos && npm install --save-dev @connectrpc/protoc-gen-connect-query @bufbuild/protoc-gen-es
|
||||
cd internal/frontend/holos && npm install @connectrpc/connect-query @bufbuild/protobuf
|
||||
|
||||
.PHONY: website-deps
|
||||
website-deps: ## Install Docusaurus deps for go generate
|
||||
cd doc/website && yarn install
|
||||
cd doc/website && npm install
|
||||
|
||||
.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: image # refer to .ko.yaml as well
|
||||
image: ## Container image build
|
||||
KO_DOCKER_REPO=$(DOCKER_REPO) GIT_DETAIL=$(GIT_DETAIL) GIT_SUFFIX=$(GIT_SUFFIX) ko build --platform=all --bare ./cmd/holos --tags $(GIT_DETAIL)$(GIT_SUFFIX)
|
||||
|
||||
.PHONY: deploy
|
||||
deploy: image ## DEPLOY TO PROD
|
||||
GIT_DETAIL=$(GIT_DETAIL) GIT_SUFFIX=$(GIT_SUFFIX) bash ./hack/deploy
|
||||
|
||||
.PHONY: website
|
||||
website: ## Build website
|
||||
./hack/build-website
|
||||
|
||||
.PHONY: help
|
||||
help: ## Display this help menu.
|
||||
|
||||
33
README.md
@@ -1,32 +1,3 @@
|
||||
## Holos - A Holostic Development Platform
|
||||
# k3d Platform
|
||||
|
||||
<img width="50%"
|
||||
align="right"
|
||||
style="display: block; margin: 40px auto;"
|
||||
src="https://openinfrastructure.co/blog/2016/02/27/logo/logorectangle.png">
|
||||
|
||||
Building and maintaining a software development platform is a complex and time
|
||||
consuming endeavour. Organizations often dedicate a team of 3-4 who need 6-12
|
||||
months to build the platform.
|
||||
|
||||
Holos is a tool and a reference platform to reduce the compexity and speed up
|
||||
the process of building a modern, cloud native software development platform.
|
||||
|
||||
- **Accelerate new projects** - Reduce time to market and operational complexity by starting your new project on top of the Holos reference platform.
|
||||
- **Modernize existing projects** - Incrementally incorporate your existing platform services into Holos for simpler integration.
|
||||
- **Unified configuration model** - Increase safety and reduce the risk of config changes with CUE.
|
||||
- **First class Helm and Kustomize support** - Leverage and reuse your existing investment in existing configuration tools such as Helm and Kustomize.
|
||||
- **Modern Authentication and Authorization** - Holos seamlessly integrates platform identity and access mangement with zero-trust beyond corp style authorization policy.
|
||||
|
||||
## Quick Installation
|
||||
```console
|
||||
go install github.com/holos-run/holos/cmd/holos@latest
|
||||
```
|
||||
|
||||
## Docs and Support
|
||||
The documentation for developing and using Holos is avaialble at: https://holos.run
|
||||
|
||||
For discussion and support, [open a discussion](https://github.com/orgs/holos-run/discussions/new/choose).
|
||||
|
||||
## License
|
||||
Holos is licensed under Apache 2.0 as found in the [LICENSE file](LICENSE).
|
||||
Refer to https://holos.run/docs/tutorial/local/k3d
|
||||
|
||||
237
Tiltfile
@@ -1,5 +1,5 @@
|
||||
# -*- mode: Python -*-
|
||||
# This Tiltfile manages a Go project with live leload in Kubernetes
|
||||
# This Tiltfile manages a Go project with live reload in Kubernetes
|
||||
|
||||
listen_port = 3000
|
||||
metrics_port = 9090
|
||||
@@ -8,56 +8,21 @@ metrics_port = 9090
|
||||
if os.getenv('TILT_WRAPPER') != '1':
|
||||
fail("could not run, ./hack/tilt/bin/tilt was not used to start tilt")
|
||||
|
||||
# AWS Account to work in
|
||||
aws_account = '271053619184'
|
||||
aws_region = 'us-east-2'
|
||||
|
||||
# Resource ids
|
||||
holos_backend = 'Holos Backend'
|
||||
pg_admin = 'pgAdmin'
|
||||
pg_cluster = 'PostgresCluster'
|
||||
pg_svc = 'Database Pod'
|
||||
holos_backend = 'Holos Server'
|
||||
compile_id = 'Go Build'
|
||||
auth_id = 'Auth Policy'
|
||||
lint_id = 'Run Linters'
|
||||
tests_id = 'Run Tests'
|
||||
|
||||
# PostgresCluster resource name in k8s
|
||||
pg_cluster_name = 'holos'
|
||||
# Database name inside the PostgresCluster
|
||||
pg_database_name = 'holos'
|
||||
# PGAdmin name
|
||||
pg_admin_name = 'pgadmin'
|
||||
|
||||
# Default Registry.
|
||||
# See: https://github.com/tilt-dev/tilt.build/blob/master/docs/choosing_clusters.md#manual-configuration
|
||||
# Note, Tilt will append the image name to the registry uri path
|
||||
default_registry('{account}.dkr.ecr.{region}.amazonaws.com/holos-run/holos-server'.format(account=aws_account, region=aws_region))
|
||||
# default_registry('{account}.dkr.ecr.{region}.amazonaws.com/holos-run/holos'.format(account=aws_account, region=aws_region))
|
||||
|
||||
# Set a name prefix specific to the user. Multiple developers share the tilt-holos namespace.
|
||||
developer = os.getenv('USER')
|
||||
holos_server = 'holos'
|
||||
# See ./hack/tilt/bin/tilt
|
||||
namespace = os.getenv('NAMESPACE')
|
||||
# We always develop against the k1 cluster.
|
||||
|
||||
# We always develop against the k3d-workload cluster
|
||||
os.putenv('KUBECONFIG', os.path.abspath('./hack/tilt/kubeconfig'))
|
||||
# The context defined in ./hack/tilt/kubeconfig
|
||||
allow_k8s_contexts('sso@k1')
|
||||
allow_k8s_contexts('sso@k2')
|
||||
allow_k8s_contexts('sso@k3')
|
||||
allow_k8s_contexts('sso@k4')
|
||||
allow_k8s_contexts('sso@k5')
|
||||
# PG db connection for localhost -> k8s port-forward
|
||||
os.putenv('PGHOST', 'localhost')
|
||||
os.putenv('PGPORT', '15432')
|
||||
# We always develop in the dev aws account.
|
||||
os.putenv('AWS_CONFIG_FILE', os.path.abspath('./hack/tilt/aws.config'))
|
||||
os.putenv('AWS_ACCOUNT', aws_account)
|
||||
os.putenv('AWS_DEFAULT_REGION', aws_region)
|
||||
os.putenv('AWS_PROFILE', 'dev-holos')
|
||||
os.putenv('AWS_SDK_LOAD_CONFIG', '1')
|
||||
# Authenticate to AWS ECR when tilt up is run by the developer
|
||||
local_resource('AWS Credentials', './hack/tilt/aws-login.sh', auto_init=True)
|
||||
|
||||
# Extensions are open-source, pre-packaged functions that extend Tilt
|
||||
#
|
||||
@@ -81,8 +46,8 @@ developer_paths = [
|
||||
'./service/holos',
|
||||
]
|
||||
|
||||
# Builds the holos-server executable
|
||||
local_resource(compile_id, 'make build', deps=developer_paths)
|
||||
# Builds the holos executable GOOS=linux
|
||||
local_resource(compile_id, 'make linux', deps=developer_paths)
|
||||
|
||||
# Build Docker image
|
||||
# Tilt will automatically associate image builds with the resource(s)
|
||||
@@ -91,84 +56,33 @@ local_resource(compile_id, 'make build', deps=developer_paths)
|
||||
# More info: https://docs.tilt.dev/api.html#api.docker_build
|
||||
#
|
||||
docker_build_with_restart(
|
||||
'holos',
|
||||
'k3d-registry.holos.localhost:5100/holos',
|
||||
context='.',
|
||||
entrypoint=[
|
||||
'/app/bin/holos',
|
||||
'/app/bin/holos.linux',
|
||||
'server',
|
||||
'--listen-port={}'.format(listen_port),
|
||||
'--oidc-issuer=https://login.ois.run',
|
||||
'--oidc-audience=262096764402729854@holos_platform',
|
||||
'--log-level=debug',
|
||||
'--metrics-port={}'.format(metrics_port),
|
||||
'--log-format=text',
|
||||
'--oidc-issuer=https://login.holos.run',
|
||||
'--oidc-audience=275804490387516853@holos_quickstart', # auth proxy
|
||||
'--oidc-audience=270319630705329162@holos_platform', # holos cli
|
||||
],
|
||||
dockerfile='./hack/tilt/Dockerfile',
|
||||
dockerfile='./Dockerfile',
|
||||
only=['./bin'],
|
||||
# (Recommended) Updating a running container in-place
|
||||
# https://docs.tilt.dev/live_update_reference.html
|
||||
live_update=[
|
||||
# Sync files from host to container
|
||||
sync('./bin', '/app/bin'),
|
||||
# Wait for aws-login https://github.com/tilt-dev/tilt/issues/3048
|
||||
sync('./tilt/aws-login.last', '/dev/null'),
|
||||
# Execute commands in the container when paths change
|
||||
# run('/app/hack/codegen.sh', trigger=['./app/api'])
|
||||
sync('./bin/', '/app/bin/'),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
# Run local commands
|
||||
# Local commands can be helpful for one-time tasks like installing
|
||||
# project prerequisites. They can also manage long-lived processes
|
||||
# for non-containerized services or dependencies.
|
||||
#
|
||||
# More info: https://docs.tilt.dev/local_resource.html
|
||||
#
|
||||
# local_resource('install-helm',
|
||||
# cmd='which helm > /dev/null || brew install helm',
|
||||
# # `cmd_bat`, when present, is used instead of `cmd` on Windows.
|
||||
# cmd_bat=[
|
||||
# 'powershell.exe',
|
||||
# '-Noninteractive',
|
||||
# '-Command',
|
||||
# '& {if (!(Get-Command helm -ErrorAction SilentlyContinue)) {scoop install helm}}'
|
||||
# ]
|
||||
# )
|
||||
|
||||
# Teach tilt about our custom resources (Note, this may be intended for workloads)
|
||||
# k8s_kind('authorizationpolicy')
|
||||
# k8s_kind('requestauthentication')
|
||||
# k8s_kind('virtualservice')
|
||||
k8s_kind('pgadmin')
|
||||
|
||||
|
||||
# Troubleshooting
|
||||
def resource_name(id):
|
||||
print('resource: {}'.format(id))
|
||||
return id.name
|
||||
|
||||
|
||||
workload_to_resource_function(resource_name)
|
||||
|
||||
# Apply Kubernetes manifests
|
||||
# Tilt will build & push any necessary images, re-deploying your
|
||||
# resources as they change.
|
||||
#
|
||||
# More info: https://docs.tilt.dev/api.html#api.k8s_yaml
|
||||
#
|
||||
|
||||
def holos_yaml():
|
||||
"""Return a k8s Deployment personalized for the developer."""
|
||||
k8s_yaml_template = str(read_file('./hack/tilt/k8s.yaml'))
|
||||
return k8s_yaml_template.format(
|
||||
name=holos_server,
|
||||
developer=developer,
|
||||
namespace=namespace,
|
||||
listen_port=listen_port,
|
||||
metrics_port=metrics_port,
|
||||
tz=os.getenv('TZ'),
|
||||
)
|
||||
|
||||
# Customize a Kubernetes resource
|
||||
# By default, Kubernetes resource names are automatically assigned
|
||||
# based on objects in the YAML manifests, e.g. Deployment name.
|
||||
@@ -179,133 +93,18 @@ def holos_yaml():
|
||||
#
|
||||
# More info: https://docs.tilt.dev/api.html#api.k8s_resource
|
||||
#
|
||||
k8s_yaml(blob(holos_yaml()))
|
||||
k8s_yaml(blob(str(read_file('./hack/tilt/k8s/dev-holos-app/deployment.yaml'))))
|
||||
|
||||
# Backend server process
|
||||
k8s_resource(
|
||||
workload=holos_server,
|
||||
new_name=holos_backend,
|
||||
objects=[
|
||||
'{}:serviceaccount'.format(holos_server),
|
||||
'{}:servicemonitor'.format(holos_server),
|
||||
],
|
||||
objects=[],
|
||||
resource_deps=[compile_id],
|
||||
links=[
|
||||
link('https://{}.app.dev.k2.holos.run/ui/'.format(developer), "Holos Web UI")
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
# AuthorizationPolicy - Beyond Corp functionality
|
||||
k8s_resource(
|
||||
new_name=auth_id,
|
||||
objects=[
|
||||
'{}:virtualservice'.format(holos_server),
|
||||
link('https://app.holos.localhost/ui/'.format(developer), "Holos Web UI")
|
||||
],
|
||||
)
|
||||
|
||||
# Database
|
||||
# Note: Tilt confuses the backup pods with the database server pods, so this code is careful to tease the pods
|
||||
# apart so logs are streamed correctly.
|
||||
# See: https://github.com/tilt-dev/tilt.specs/blob/master/resource_assembly.md
|
||||
|
||||
# pgAdmin Web UI
|
||||
k8s_resource(
|
||||
workload=pg_admin_name,
|
||||
new_name=pg_admin,
|
||||
port_forwards=[
|
||||
port_forward(15050, 5050, pg_admin),
|
||||
],
|
||||
)
|
||||
|
||||
# Disabled because these don't group resources nicely
|
||||
# k8s_kind('postgrescluster')
|
||||
|
||||
# Postgres database in-cluster
|
||||
k8s_resource(
|
||||
new_name=pg_cluster,
|
||||
objects=['holos:postgrescluster'],
|
||||
)
|
||||
|
||||
# Needed to select the database by label
|
||||
# https://docs.tilt.dev/api.html#api.k8s_custom_deploy
|
||||
k8s_custom_deploy(
|
||||
pg_svc,
|
||||
apply_cmd=['./hack/tilt/k8s-get-db-sts', pg_cluster_name],
|
||||
delete_cmd=['echo', 'Skipping delete. Object managed by custom resource.'],
|
||||
deps=[],
|
||||
)
|
||||
k8s_resource(
|
||||
pg_svc,
|
||||
port_forwards=[
|
||||
port_forward(15432, 5432, 'psql'),
|
||||
],
|
||||
resource_deps=[pg_cluster]
|
||||
)
|
||||
|
||||
|
||||
# Run tests
|
||||
local_resource(
|
||||
tests_id,
|
||||
'make test',
|
||||
allow_parallel=True,
|
||||
auto_init=False,
|
||||
deps=developer_paths,
|
||||
)
|
||||
|
||||
# Run linter
|
||||
local_resource(
|
||||
lint_id,
|
||||
'make lint',
|
||||
allow_parallel=True,
|
||||
auto_init=False,
|
||||
deps=developer_paths,
|
||||
)
|
||||
|
||||
# UI Buttons for helpful things.
|
||||
# Icons: https://fonts.google.com/icons
|
||||
os.putenv("GH_FORCE_TTY", "80%")
|
||||
cmd_button(
|
||||
'{}:go-test-failfast'.format(tests_id),
|
||||
argv=['./hack/tilt/go-test-failfast'],
|
||||
resource=tests_id,
|
||||
icon_name='quiz',
|
||||
text='Fail Fast',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:issues'.format(holos_server),
|
||||
argv=['./hack/tilt/gh-issues'],
|
||||
resource=holos_backend,
|
||||
icon_name='folder_data',
|
||||
text='Issues',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:gh-issue-view'.format(holos_server),
|
||||
argv=['./hack/tilt/gh-issue-view'],
|
||||
resource=holos_backend,
|
||||
icon_name='task',
|
||||
text='View Issue',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:get-pgdb-creds'.format(holos_server),
|
||||
argv=['./hack/tilt/get-pgdb-creds', pg_cluster_name, pg_database_name],
|
||||
resource=pg_svc,
|
||||
icon_name='lock_open_right',
|
||||
text='DB Creds',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:get-pgdb-creds'.format(pg_admin_name),
|
||||
argv=['./hack/tilt/get-pgdb-creds', pg_cluster_name, pg_database_name],
|
||||
resource=pg_admin,
|
||||
icon_name='lock_open_right',
|
||||
text='DB Creds',
|
||||
)
|
||||
cmd_button(
|
||||
'{}:get-pgadmin-creds'.format(pg_admin_name),
|
||||
argv=['./hack/tilt/get-pgadmin-creds', pg_admin_name],
|
||||
resource=pg_admin,
|
||||
icon_name='lock_open_right',
|
||||
text='pgAdmin Login',
|
||||
)
|
||||
|
||||
print("✨ Tiltfile evaluated")
|
||||
|
||||
@@ -22,3 +22,5 @@
|
||||
// Note that Holos operates as a data pipeline, so the output of a [HelmChart]
|
||||
// may be provided to [Kustomize] for post-processing.
|
||||
package v1alpha2
|
||||
|
||||
//go:generate ../../../hack/gendoc
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
module: "github.com/holos-run/holos"
|
||||
403
doc/md/api/core/v1alpha2.md
Normal file
@@ -0,0 +1,403 @@
|
||||
<!-- Code generated by gomarkdoc. DO NOT EDIT -->
|
||||
|
||||
# v1alpha2
|
||||
|
||||
```go
|
||||
import "github.com/holos-run/holos/api/core/v1alpha2"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
[Platform](<#Platform>) defines the complete configuration of a platform. With the holos reference platform this takes the shape of one management cluster and at least two workload cluster. Each cluster has multiple [HolosComponent](<#HolosComponent>) resources applied to it.
|
||||
|
||||
Each holos component path, e.g. \`components/namespaces\` produces exactly one [BuildPlan](<#BuildPlan>) which in turn contains a set of [HolosComponent](<#HolosComponent>) kinds.
|
||||
|
||||
The primary kinds of [HolosComponent](<#HolosComponent>) are:
|
||||
|
||||
1. [HelmChart](<#HelmChart>) to render config from a helm chart.
|
||||
2. [KustomizeBuild](<#KustomizeBuild>) to render config from [Kustomize](<#Kustomize>)
|
||||
3. [KubernetesObjects](<#KubernetesObjects>) to render [APIObjects](<#APIObjects>) defined directly in CUE configuration.
|
||||
|
||||
Note that Holos operates as a data pipeline, so the output of a [HelmChart](<#HelmChart>) may be provided to [Kustomize](<#Kustomize>) for post\-processing.
|
||||
|
||||
## Index
|
||||
|
||||
- [Constants](<#constants>)
|
||||
- [type APIObject](<#APIObject>)
|
||||
- [type APIObjectMap](<#APIObjectMap>)
|
||||
- [type APIObjects](<#APIObjects>)
|
||||
- [type BuildPlan](<#BuildPlan>)
|
||||
- [type BuildPlanComponents](<#BuildPlanComponents>)
|
||||
- [type BuildPlanSpec](<#BuildPlanSpec>)
|
||||
- [type Chart](<#Chart>)
|
||||
- [type FileContent](<#FileContent>)
|
||||
- [type FileContentMap](<#FileContentMap>)
|
||||
- [type FilePath](<#FilePath>)
|
||||
- [type HelmChart](<#HelmChart>)
|
||||
- [type HolosComponent](<#HolosComponent>)
|
||||
- [type Kind](<#Kind>)
|
||||
- [type KubernetesObjects](<#KubernetesObjects>)
|
||||
- [type Kustomize](<#Kustomize>)
|
||||
- [type KustomizeBuild](<#KustomizeBuild>)
|
||||
- [type Label](<#Label>)
|
||||
- [type Metadata](<#Metadata>)
|
||||
- [type Platform](<#Platform>)
|
||||
- [type PlatformMetadata](<#PlatformMetadata>)
|
||||
- [type PlatformSpec](<#PlatformSpec>)
|
||||
- [type PlatformSpecComponent](<#PlatformSpecComponent>)
|
||||
- [type Repository](<#Repository>)
|
||||
|
||||
|
||||
## Constants
|
||||
|
||||
<a name="APIVersion"></a>
|
||||
|
||||
```go
|
||||
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"
|
||||
)
|
||||
```
|
||||
|
||||
<a name="KubernetesObjectsKind"></a>
|
||||
|
||||
```go
|
||||
const KubernetesObjectsKind = "KubernetesObjects"
|
||||
```
|
||||
|
||||
<a name="APIObject"></a>
|
||||
## type APIObject {#APIObject}
|
||||
|
||||
APIObject represents the most basic generic form of a single kubernetes api object. Represented as a JSON object internally for compatibility between tools, for example loading from CUE.
|
||||
|
||||
```go
|
||||
type APIObject structpb.Struct
|
||||
```
|
||||
|
||||
<a name="APIObjectMap"></a>
|
||||
## type APIObjectMap {#APIObjectMap}
|
||||
|
||||
APIObjectMap represents the marshalled yaml representation of kubernetes api objects. Do not produce an APIObjectMap directly, instead use [APIObjects](<#APIObjects>) to produce the marshalled yaml representation from CUE data, then provide the result to [HolosComponent](<#HolosComponent>).
|
||||
|
||||
```go
|
||||
type APIObjectMap map[Kind]map[Label]string
|
||||
```
|
||||
|
||||
<a name="APIObjects"></a>
|
||||
## type APIObjects {#APIObjects}
|
||||
|
||||
APIObjects represents Kubernetes API objects defined directly from CUE code. Useful to mix in resources to any kind of [HolosComponent](<#HolosComponent>), for example adding an ExternalSecret resource to a [HelmChart](<#HelmChart>).
|
||||
|
||||
[Kind](<#Kind>) must be the resource kind, e.g. Deployment or Service.
|
||||
|
||||
[Label](<#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](<#HolosComponent>) which accepts an [APIObjectMap](<#APIObjectMap>) field provided by [APIObjects](<#APIObjects>).
|
||||
|
||||
```go
|
||||
type APIObjects struct {
|
||||
APIObjects map[Kind]map[Label]APIObject `json:"apiObjects"`
|
||||
APIObjectMap APIObjectMap `json:"apiObjectMap"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="BuildPlan"></a>
|
||||
## type BuildPlan {#BuildPlan}
|
||||
|
||||
BuildPlan represents a build plan for the holos cli to execute. The purpose of a BuildPlan is to define one or more [HolosComponent](<#HolosComponent>) kinds. For example a [HelmChart](<#HelmChart>), [KustomizeBuild](<#KustomizeBuild>), or [KubernetesObjects](<#KubernetesObjects>).
|
||||
|
||||
A BuildPlan usually has an additional empty [KubernetesObjects](<#KubernetesObjects>) for the purpose of using the [HolosComponent](<#HolosComponent>) DeployFiles field to deploy an ArgoCD or Flux gitops resource for the holos component.
|
||||
|
||||
```go
|
||||
type BuildPlan struct {
|
||||
Kind string `json:"kind" cue:"\"BuildPlan\""`
|
||||
APIVersion string `json:"apiVersion" cue:"string | *\"v1alpha2\""`
|
||||
Spec BuildPlanSpec `json:"spec"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="BuildPlanComponents"></a>
|
||||
## type BuildPlanComponents {#BuildPlanComponents}
|
||||
|
||||
|
||||
|
||||
```go
|
||||
type BuildPlanComponents struct {
|
||||
Resources map[Label]KubernetesObjects `json:"resources,omitempty"`
|
||||
KubernetesObjectsList []KubernetesObjects `json:"kubernetesObjectsList,omitempty"`
|
||||
HelmChartList []HelmChart `json:"helmChartList,omitempty"`
|
||||
KustomizeBuildList []KustomizeBuild `json:"kustomizeBuildList,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="BuildPlanSpec"></a>
|
||||
## type BuildPlanSpec {#BuildPlanSpec}
|
||||
|
||||
BuildPlanSpec represents the specification of the build plan.
|
||||
|
||||
```go
|
||||
type BuildPlanSpec struct {
|
||||
// Disabled causes the holos cli to take no action over the [BuildPlan].
|
||||
Disabled bool `json:"disabled,omitempty"`
|
||||
// Components represents multiple [HolosComponent] kinds to manage.
|
||||
Components BuildPlanComponents `json:"components,omitempty"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Chart"></a>
|
||||
## type Chart {#Chart}
|
||||
|
||||
Chart represents a helm chart.
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="FileContent"></a>
|
||||
## type FileContent {#FileContent}
|
||||
|
||||
FileContent represents file contents.
|
||||
|
||||
```go
|
||||
type FileContent string
|
||||
```
|
||||
|
||||
<a name="FileContentMap"></a>
|
||||
## type FileContentMap {#FileContentMap}
|
||||
|
||||
FileContentMap represents a mapping of file paths to file contents. Paths are relative to the \`holos\` output "deploy" directory, and may contain sub\-directories.
|
||||
|
||||
```go
|
||||
type FileContentMap map[FilePath]FileContent
|
||||
```
|
||||
|
||||
<a name="FilePath"></a>
|
||||
## type FilePath {#FilePath}
|
||||
|
||||
FilePath represents a file path.
|
||||
|
||||
```go
|
||||
type FilePath string
|
||||
```
|
||||
|
||||
<a name="HelmChart"></a>
|
||||
## type HelmChart {#HelmChart}
|
||||
|
||||
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](<#HolosComponent>), for example [Kustomize](<#Kustomize>) post\-rendering and mixing in additional kubernetes api objects.
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="HolosComponent"></a>
|
||||
## type HolosComponent {#HolosComponent}
|
||||
|
||||
HolosComponent defines the fields common to all holos component kinds. Every holos component kind should embed HolosComponent.
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Kind"></a>
|
||||
## type Kind {#Kind}
|
||||
|
||||
Kind is a kubernetes api object kind. Defined as a type for clarity and type checking.
|
||||
|
||||
```go
|
||||
type Kind string
|
||||
```
|
||||
|
||||
<a name="KubernetesObjects"></a>
|
||||
## type KubernetesObjects {#KubernetesObjects}
|
||||
|
||||
KubernetesObjects represents a [HolosComponent](<#HolosComponent>) composed of Kubernetes API objects provided directly from CUE using [APIObjects](<#APIObjects>).
|
||||
|
||||
```go
|
||||
type KubernetesObjects struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"KubernetesObjects\""`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Kustomize"></a>
|
||||
## type Kustomize {#Kustomize}
|
||||
|
||||
Kustomize represents resources necessary to execute a kustomize build. Intended for at least two use cases:
|
||||
|
||||
1. Process a [KustomizeBuild](<#KustomizeBuild>) [HolosComponent](<#HolosComponent>) which represents raw yaml file resources in a holos component directory.
|
||||
2. Post process a [HelmChart](<#HelmChart>) [HolosComponent](<#HolosComponent>) to inject istio, patch jobs, add custom labels, etc...
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="KustomizeBuild"></a>
|
||||
## type KustomizeBuild {#KustomizeBuild}
|
||||
|
||||
KustomizeBuild represents a [HolosComponent](<#HolosComponent>) that renders plain yaml files in the holos component directory using \`kubectl kustomize build\`.
|
||||
|
||||
```go
|
||||
type KustomizeBuild struct {
|
||||
HolosComponent `json:",inline"`
|
||||
Kind string `json:"kind" cue:"\"KustomizeBuild\""`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Label"></a>
|
||||
## type Label {#Label}
|
||||
|
||||
Label is an arbitrary unique identifier internal to holos itself. The holos cli is expected to never write a Label value to rendered output files, therefore use a [Label](<#Label>) then the identifier must be unique and internal. Defined as a type for clarity and type checking.
|
||||
|
||||
A Label is useful to convert a CUE struct to a list, for example producing a list of [APIObject](<#APIObject>) resources from an [APIObjectMap](<#APIObjectMap>). A CUE struct using Label keys is guaranteed to not lose data when rendering output because a Label is expected to never be written to the final output.
|
||||
|
||||
```go
|
||||
type Label string
|
||||
```
|
||||
|
||||
<a name="Metadata"></a>
|
||||
## type Metadata {#Metadata}
|
||||
|
||||
Metadata represents data about the holos component such as the Name.
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Platform"></a>
|
||||
## type Platform {#Platform}
|
||||
|
||||
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.
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="PlatformMetadata"></a>
|
||||
## type PlatformMetadata {#PlatformMetadata}
|
||||
|
||||
|
||||
|
||||
```go
|
||||
type PlatformMetadata struct {
|
||||
// Name represents the Platform name.
|
||||
Name string `json:"name"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="PlatformSpec"></a>
|
||||
## type PlatformSpec {#PlatformSpec}
|
||||
|
||||
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.
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="PlatformSpecComponent"></a>
|
||||
## type PlatformSpecComponent {#PlatformSpecComponent}
|
||||
|
||||
PlatformSpecComponent represents a holos component to build or render.
|
||||
|
||||
```go
|
||||
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"`
|
||||
}
|
||||
```
|
||||
|
||||
<a name="Repository"></a>
|
||||
## type Repository {#Repository}
|
||||
|
||||
Repository represents a helm chart repository.
|
||||
|
||||
```go
|
||||
type Repository struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
}
|
||||
```
|
||||
|
||||
Generated by [gomarkdoc](<https://github.com/princjef/gomarkdoc>)
|
||||
3
doc/md/cli.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# CLI
|
||||
|
||||
Use the `holos` command line interface (CLI) to render individual platform components, entire platforms, and push/pull the platform model.
|
||||
47
doc/md/design/rendering.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Rendering
|
||||
|
||||
:::tip
|
||||
|
||||
This document provides a brief overview of the rendering process, a core design
|
||||
element in Holos.
|
||||
|
||||
:::
|
||||
|
||||
Holos uses the Kubernetes resource model to manage configuration. The `holos`
|
||||
command line interface is the primary method you'll use to manage your platform.
|
||||
Holos uses CUE to provide a unified configuration model of the platform. This
|
||||
unified configuration is built up from components packaged with Helm, Kustomize,
|
||||
CUE, or any other tool that can produce Kubernetes resource manifests as output.
|
||||
|
||||
This process can be thought of as a data **rendering pipeline**. The key
|
||||
concept is that `holos` will always produce fully rendered output, but delegates
|
||||
the _application_ of the configuration to other tools like `kubectl apply`,
|
||||
ArgoCD, or Flux.
|
||||
|
||||
```mermaid
|
||||
---
|
||||
title: Figure 2 - Render Pipeline
|
||||
---
|
||||
graph LR
|
||||
PS[<a href="/docs/api/core/v1alpha2#PlatformSpec">PlatformSpec</a>]
|
||||
BP[<a href="/docs/api/core/v1alpha2#BuildPlan">BuildPlan</a>]
|
||||
HC[<a href="/docs/api/core/v1alpha2#HolosComponent">HolosComponent</a>]
|
||||
|
||||
H[<a href="/docs/api/core/v1alpha2#HelmChart">HelmChart</a>]
|
||||
K[<a href="/docs/api/core/v1alpha2#KustomizeBuild">KustomizeBuild</a>]
|
||||
O[<a href="/docs/api/core/v1alpha2#KubernetesObjects">KubernetesObjects</a>]
|
||||
|
||||
P[<a href="/docs/api/core/v1alpha2#Kustomize">Kustomize</a>]
|
||||
Y[Kubernetes <br>Resources]
|
||||
G[GitOps <br>Resource]
|
||||
|
||||
C[Kube API Server]
|
||||
|
||||
PS --> BP --> HC
|
||||
HC --> H --> P
|
||||
HC --> K --> P
|
||||
HC --> O --> P
|
||||
|
||||
P --> Y --> C
|
||||
P --> G --> C
|
||||
```
|
||||
88
doc/md/glossary.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# Glossary
|
||||
|
||||
This page describes the terms used within the context of Holos.
|
||||
|
||||
## Platform
|
||||
|
||||
In Holos, a Platform is a comprehensive environment configured using the
|
||||
Kubernetes resource model. It extends beyond traditional Kubernetes
|
||||
functionality by integrating cloud resources through Crossplane, allowing for a
|
||||
unified management approach across both Kubernetes and cloud infrastructure. A
|
||||
Platform typically consists of one Management Cluster, which handles control and
|
||||
secret management, and one or more Workload Clusters, where application
|
||||
workloads are deployed and run. This architecture enables a consistent and
|
||||
scalable approach to managing diverse resources and services within the
|
||||
cloud-native ecosystem.
|
||||
|
||||
## Management Cluster
|
||||
|
||||
In the context of Holos, a Management Cluster is a special Kubernetes cluster
|
||||
that hosts Kubernetes controllers. For example, cert-manager, Cluster api, and
|
||||
Crossplane. A management cluster manages a single platform. The primary
|
||||
function of this cluster is to securely store and manage secrets, ensuring the
|
||||
secure handling of sensitive information such as credentials, API keys, and
|
||||
other confidential data. The Management Cluster serves as a centralized and
|
||||
secure control plane for the platform, facilitating the orchestration and
|
||||
management of other components.
|
||||
|
||||
## Workload Cluster
|
||||
|
||||
In Holos, a Workload Cluster is a Kubernetes cluster designed to host and run
|
||||
application workloads. Unlike the Management Cluster, which focuses on control
|
||||
and secret management, Workload Clusters are dedicated to executing the actual
|
||||
applications and services. These clusters can vary in size and configuration
|
||||
based on the specific needs of the applications they support. Workload Clusters
|
||||
leverage Kubernetes' orchestration capabilities to manage the deployment,
|
||||
scaling, and operation of containerized applications, providing a flexible and
|
||||
scalable environment for running production workloads within the platform.
|
||||
|
||||
## Platform Form
|
||||
|
||||
In Holos, a Platform Form is a customizable web form defined by JSON data. Each
|
||||
platform within Holos has a unique Platform Form, which serves as an interface
|
||||
for configuring and managing the platform's settings and resources. Platform
|
||||
engineers can customize the Platform Form by modifying the underlying CUE
|
||||
(Configuration Unified Engine) code, allowing for tailored configurations that
|
||||
meet specific requirements. This flexibility enables platform engineers to
|
||||
create a user-friendly and specific interface for managing the platform's
|
||||
components and operations.
|
||||
|
||||
## Platform Model
|
||||
|
||||
In Holos, the Platform Model represents the collection of values submitted
|
||||
through the Platform Form. It encapsulates the specific configuration details
|
||||
and settings defined by the platform engineers, serving as the blueprint for the
|
||||
platform's setup and operation. The Platform Model is essential for translating
|
||||
the customized options and parameters from the Platform Form into actionable
|
||||
configurations within the Holos ecosystem, ensuring that the platform operates
|
||||
according to the specified requirements and guidelines.
|
||||
|
||||
## Secret Store
|
||||
|
||||
In Holos, a SecretStore is a repository for securely storing and managing
|
||||
sensitive data such as passwords, API keys, and other confidential information.
|
||||
It is compatible with any secret store supported by the External Secrets
|
||||
Operator. By default, the management cluster serves as the SecretStore to
|
||||
minimize dependencies and simplify the architecture. This setup ensures that
|
||||
secrets are managed in a secure and centralized manner, aligning with the
|
||||
overall security framework of the platform.
|
||||
|
||||
## Service Mesh
|
||||
|
||||
In Holos, a Service Mesh is a dedicated infrastructure layer for managing,
|
||||
observing, and securing service-to-service communications within a microservices
|
||||
architecture. It typically includes features such as load balancing, traffic
|
||||
routing, service discovery, and security policies like mutual TLS and access
|
||||
control. The Service Mesh abstracts these functionalities away from the
|
||||
application code, providing a centralized control plane for managing the
|
||||
interactions between microservices. This facilitates better observability,
|
||||
resilience, and security in complex, distributed environments.
|
||||
|
||||
## Zero Trust
|
||||
|
||||
In the context of Holos and broader security practices, Zero Trust is a security
|
||||
model that assumes no implicit trust is granted to any user, system, or
|
||||
component inside or outside the network. Instead, every request for access is
|
||||
treated as potentially malicious, and verification is required at every stage.
|
||||
This model enforces strict identity verification, continuous monitoring, and
|
||||
least-privilege access policies.
|
||||
BIN
doc/md/guides/argocd/argocd-apps-2.png
Normal file
|
After Width: | Height: | Size: 934 KiB |
BIN
doc/md/guides/argocd/argocd-apps.png
Normal file
|
After Width: | Height: | Size: 703 KiB |
BIN
doc/md/guides/argocd/argocd-auto-sync-ok.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
doc/md/guides/argocd/argocd-diff.png
Normal file
|
After Width: | Height: | Size: 1014 KiB |
BIN
doc/md/guides/argocd/argocd-login-signedout.png
Normal file
|
After Width: | Height: | Size: 728 KiB |
BIN
doc/md/guides/argocd/argocd-login.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
doc/md/guides/argocd/argocd-out-of-sync.png
Normal file
|
After Width: | Height: | Size: 1014 KiB |
BIN
doc/md/guides/argocd/argocd-sync-ok.png
Normal file
|
After Width: | Height: | Size: 854 KiB |
BIN
doc/md/guides/argocd/argocd-sync.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
6
doc/md/guides/argocd/index.mdx
Normal file
@@ -0,0 +1,6 @@
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# ArgoCD
|
||||
|
||||
Coming soon.
|
||||
3
doc/md/guides/backstage/index.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Backstage
|
||||
|
||||
Coming soon.
|
||||
21
doc/md/guides/install.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Install Holos
|
||||
|
||||
Holos is distributed as a single file executable.
|
||||
|
||||
## Releases
|
||||
|
||||
Download `holos` from the [releases](https://github.com/holos-run/holos/releases) page and place the executable into your shell path.
|
||||
|
||||
## Go install
|
||||
|
||||
Alternatively, install directly into your go bin path using:
|
||||
|
||||
```shell
|
||||
go install github.com/holos-run/holos/cmd/holos@latest
|
||||
```
|
||||
|
||||
### What you'll need
|
||||
|
||||
- [helm](https://github.com/helm/helm/releases) to fetch and render Helm chart components.
|
||||
- [kubectl](https://kubernetes.io/docs/tasks/tools/) to [kustomize](https://kustomize.io/) components.
|
||||
|
||||
3
doc/md/guides/observability/index.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Observability
|
||||
|
||||
Coming soon.
|
||||
17
doc/md/guides/overview.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Overview
|
||||
|
||||
<!-- https://kubernetes.io/docs/contribute/style/diagram-guide/ -->
|
||||
|
||||
This tutorial covers the following process of getting started with Holos.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[1. Install <br>holos] -->
|
||||
B[2. Register <br>account] -->
|
||||
C[3. Generate <br>platform] -->
|
||||
D[4. Render <br>platform] -->
|
||||
E[5. Apply <br>config]
|
||||
|
||||
classDef box fill:#fff,stroke:#000,stroke-width:1px,color:#000;
|
||||
class A,B,C,D,E box
|
||||
```
|
||||
62
doc/md/guides/register.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# Registration
|
||||
|
||||
Holos leverages a simple web app to collect and store platform attributes with a web form. Register an account with the web app to create and retrieve the platform model.
|
||||
|
||||
```
|
||||
holos register user
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
Holos allows you to customize all of the sections and fields of your platform model.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
## Generate your Platform
|
||||
|
||||
Generate your platform configuration from the holos reference platform embedded in the `holos` executable. Platform configuration is stored in a git repository.
|
||||
|
||||
```bash
|
||||
mkdir holos-infra
|
||||
cd holos-infra
|
||||
holos generate platform holos
|
||||
```
|
||||
|
||||
The generate command writes many files organized by platform component into the current directory
|
||||
|
||||
TODO: Put a table here describing key elements?
|
||||
|
||||
:::tip
|
||||
|
||||
Take a peek at `holos generate platform --help` to see other platforms embedded in the holos executable.
|
||||
|
||||
:::
|
||||
|
||||
## Push the Platform Form
|
||||
|
||||
```
|
||||
holos push platform form .
|
||||
```
|
||||
|
||||
## Fill in the form
|
||||
|
||||
TODO
|
||||
|
||||
## Pull the Platform Model
|
||||
|
||||
Once the platform model is saved, pull it into the holos-infra repository:
|
||||
|
||||
```
|
||||
holos pull platform model .
|
||||
```
|
||||
|
||||
## Render the Platform
|
||||
|
||||
With the platform model and the platform spec, you're ready to render the complete platform configuration:
|
||||
|
||||
```
|
||||
holos render platform ./platform
|
||||
```
|
||||
|
||||
## Summary
|
||||
BIN
doc/md/guides/try-holos/form-pushed.png
Normal file
|
After Width: | Height: | Size: 624 KiB |
BIN
doc/md/guides/try-holos/form.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
657
doc/md/guides/try-holos/index.mdx
Normal file
@@ -0,0 +1,657 @@
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# Try Holos Locally
|
||||
|
||||
This guide walks through the process of building and managing a software
|
||||
development platform with Holos. The k3d platform built in this guide is a
|
||||
slimmed down version of the larger, more holistic, Holos reference platform.
|
||||
|
||||
Holos is different from existing tools in a few important ways.
|
||||
|
||||
1. Holos provides a **unified configuration model** purpose built to improve on
|
||||
unmodified Helm charts, Kustomize bases, or anything else that produces
|
||||
structured configuration data.
|
||||
2. Holos all but **eliminates the need to template yaml**, a common source of
|
||||
frustration and errors in production.
|
||||
3. Holos platforms are **composable** and have breadth. The toolchain and
|
||||
techniques scale down to one machine and up to multiple clusters across
|
||||
multiple regions.
|
||||
4. The unified configuration model is well suited to a **Zero Trust security
|
||||
model**. Platform wide policy configuration is easier to manage with Holos.
|
||||
|
||||
---
|
||||
|
||||
This guide assumes commands are run locally. Capitalized terms have specific
|
||||
definitions described in the [Glossary](/docs/glossary).
|
||||
|
||||
## What you'll need {#Requirements}
|
||||
|
||||
You'll need the following tools installed to complete this guide.
|
||||
|
||||
1. [holos](/docs/guides/install) - to build the platform.
|
||||
2. [helm](https://helm.sh/docs/intro/install/) - to render Holos components that wrap upstream Helm charts.
|
||||
3. [k3d](https://k3d.io/#installation) - to provide a k8s api server.
|
||||
4. [OrbStack](https://docs.orbstack.dev/install) or [Docker](https://docs.docker.com/get-docker/) - to use k3d.
|
||||
5. [kubectl](https://kubernetes.io/docs/tasks/tools/) - to interact with the k8s api server.
|
||||
6. [mkcert](https://github.com/FiloSottile/mkcert?tab=readme-ov-file#installation) - to make trusted TLS certificates.
|
||||
7. [jq](https://jqlang.github.io/jq/download/) - to fiddle with JSON output.
|
||||
|
||||
:::note
|
||||
|
||||
Registering an account **is recommended** to try out proper authentication and
|
||||
authorization in Holos, but you can complete this guide without signing up.
|
||||
|
||||
:::
|
||||
|
||||
## Goal {#Goal}
|
||||
|
||||
By the end of this guide you'll have built the foundation of a software
|
||||
development platform. The foundation provides Zero Trust security by
|
||||
holistically integrating off-the-shelf open source software.
|
||||
|
||||
1. Istio is configured to authenticate and authorize requests using an OIDC
|
||||
ID-Token issued by ZITADEL before requests reach backend services.
|
||||
2. The platform provides single sign-on and role based access control for all
|
||||
services running on the platform.
|
||||
|
||||
This guide strives to keep things neat and tidy. All of the resources are
|
||||
located in one k3d cluster and one local Git repository. If you want to clean
|
||||
up at any point, do so with:
|
||||
|
||||
```bash
|
||||
k3d cluster delete workload
|
||||
rm -rf holos-k3d
|
||||
```
|
||||
|
||||
## Sign In or Out {#Sign-In}
|
||||
|
||||
Holos provides integrated authentication and authorization which we'll use in
|
||||
this guide to protect a service. We recommend registering an account to see
|
||||
this in action. Registration also enables you to explore the customizable web
|
||||
form that simplifies complex configuration.
|
||||
|
||||
If you opt-out, the platform will be configured to use a fake identity in place
|
||||
of real id tokens.
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Sign In">
|
||||
```bash
|
||||
holos register user
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Opt Out">
|
||||
```bash
|
||||
holos logout
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Create the Platform {#Create-Platform}
|
||||
|
||||
A server-side platform resource in Holos stores the web form used to simplify
|
||||
platform wide configuration.
|
||||
|
||||
First, initialize an empty Git repository:
|
||||
|
||||
```bash
|
||||
mkdir holos-k3d
|
||||
cd holos-k3d
|
||||
git init
|
||||
```
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Signed In">
|
||||
Use `holos` to make the rpc call to create the server-side platform
|
||||
resource.
|
||||
|
||||
```bash
|
||||
holos create platform --name k3d --display-name "Try Holos Locally"
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Signed Out">
|
||||
Create a blank `platform.metadata.json` file so subsequent holos commands
|
||||
skip rpc calls.
|
||||
|
||||
```bash
|
||||
touch platform.metadata.json
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Generate the Platform {#Generate-Platform}
|
||||
|
||||
Generate the platform code in the repository root.
|
||||
|
||||
```bash
|
||||
holos generate platform k3d
|
||||
```
|
||||
|
||||
Commit the generated platform config to the repository.
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "holos generate platform k3d - $(holos --version)"
|
||||
```
|
||||
|
||||
### Push the Platform Form
|
||||
|
||||
Each Holos platform has a Platform Form used to submit top level, platform-wide
|
||||
configuration values. The purpose of the form is to validate configuration
|
||||
values and simplify complicated configurations and integrations.
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Signed In">
|
||||
Push the Platform Form to publish it. Browse to the printed URL to view the
|
||||
form.
|
||||
|
||||
```bash
|
||||
holos push platform form .
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Signed Out">
|
||||
You will update the Platform Model locally in a later step so there's
|
||||
nothing to do in this step. Only signed-in users can push a Platform Form
|
||||
to the Holos web server.
|
||||
|
||||
```bash
|
||||
# holos push platform form .
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
The Platform Form is defined locally in `forms/platform/platform-form.cue`.
|
||||
|
||||
On the web it looks like:
|
||||

|
||||
|
||||
### Update the Platform Model {#Platform-Model}
|
||||
|
||||
Holos needs initial, top level configuration values to render the platform. The
|
||||
Platform Model is the term we use for these values. In this section you will
|
||||
configure role based access control by way of updating the Platform Model.
|
||||
|
||||
In the k3d platform you're building now, role based access control is
|
||||
implemented by asserting against the oidc id token subject. Update the form
|
||||
with the `sub` claim value from your id token. This will ensure only you have
|
||||
access to platform services.
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Signed In">
|
||||
Copy and paste the `sub` value into your Platform Form's Subject field.
|
||||
|
||||
```bash
|
||||
holos login --print-claims --log-level=error | jq -r .sub
|
||||
```
|
||||
|
||||
After pasting the `sub` value, click Submit on the form.
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Signed Out">
|
||||
You don't have an id token when you're signed out, so there's nothing for
|
||||
you to do in this step.
|
||||
|
||||
```bash
|
||||
# holos login --print-claims --log-level=error | jq -r .sub
|
||||
```
|
||||
|
||||
The platform will be configured to assert against the User-Agent header
|
||||
instead.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Pull the Platform Model {#Pull-the-Platform-Model}
|
||||
|
||||
The Platform Model needs to be pulled into the local Git repository after the
|
||||
form has been submitted. Next, we'll run `holos render` which operates
|
||||
exclusively on local files.
|
||||
|
||||
Holos stores the Platform Model in the `platform.config.json` file. Holos
|
||||
provides this file as input to CUE when rendering the platform. This file is
|
||||
intended to be added to version control.
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Signed In">
|
||||
Pull the updated Platform Model into the local repository.
|
||||
|
||||
```bash
|
||||
holos pull platform model .
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Signed Out">
|
||||
The `holos generate platform k3d` command should have created a default Platform
|
||||
Model in `platform.config.json`. As a result there's nothing to do in this
|
||||
step.
|
||||
|
||||
```bash
|
||||
# holos pull platform model .
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
```bash
|
||||
git add platform.config.json
|
||||
git commit -m "Add platform model"
|
||||
```
|
||||
|
||||
## Render the Platform {#Render-the-Platform}
|
||||
|
||||
Holos has everything necessary to render the platform once the
|
||||
`platform.config.json` file and the code from `holos generate` are in the
|
||||
current directory.
|
||||
|
||||
Rendering a platform is the process of iterating over each platform component
|
||||
and rendering it into plain yaml. Holos does not apply the resulting manifests.
|
||||
Other tools like kubectl, ArgoCD, or Flux are responsible for applying the
|
||||
manifests.
|
||||
|
||||
```bash
|
||||
holos render platform ./platform
|
||||
```
|
||||
|
||||
The render command writes the manifest files to the `deploy/` directory. Commit
|
||||
the files so they can be applied via GitOps later.
|
||||
|
||||
```bash
|
||||
git add deploy
|
||||
git commit -m "holos render platform ./platform"
|
||||
```
|
||||
|
||||
:::important
|
||||
|
||||
⚡ Don't blink, this is where Holos actually builds the platform. It usually
|
||||
takes no more than a few seconds.
|
||||
|
||||
Rendering the holos reference platform currently results in about 500K lines of
|
||||
yaml. In contrast, roughly 80K lines are produced by this slimmed down k3d
|
||||
platform.
|
||||
|
||||
We mention this because the scale doesn't matter as much as it does with other
|
||||
tools. Manage millions of lines of configuration with Holos the same way this
|
||||
guide manages thousands. This is made possible by the unique way CUE unifies
|
||||
all configuration into one single model.
|
||||
|
||||
:::
|
||||
|
||||
## Create the Cluster {#Create-Cluster}
|
||||
|
||||
The Workload Cluster is where your applications and services will be deployed.
|
||||
In production this is usually an EKS, GKE, or AKS cluster.
|
||||
|
||||
:::tip
|
||||
|
||||
Holos supports all compliant Kubernetes clusters. Holos was developed and tested
|
||||
on GKE, EKS, Talos, k3s, and Kubeadm clusters.
|
||||
|
||||
:::
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="evaluate" label="Try Holos" default>
|
||||
Use this command when exploring Holos.
|
||||
|
||||
```bash
|
||||
k3d cluster create workload \
|
||||
--port "443:443@loadbalancer" \
|
||||
--k3s-arg "--disable=traefik@server:0"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="develop" label="Develop Holos">
|
||||
Use this command when developing Holos.
|
||||
|
||||
```bash
|
||||
k3d registry create registry.holos.localhost --port 5100
|
||||
```
|
||||
|
||||
```bash
|
||||
k3d cluster create workload \
|
||||
--registry-use k3d-registry.holos.localhost:5100 \
|
||||
--port "443:443@loadbalancer" \
|
||||
--k3s-arg "--disable=traefik@server:0"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
Traefik is disabled because Istio provides the same functionality.
|
||||
|
||||
### Local CA {#Local-CA}
|
||||
|
||||
Holos platforms use cert manager to issue tls certificates. The browser and
|
||||
tools we're using need to trust these certificates to work together. In this
|
||||
section we'll create a local trusted certificate authority.
|
||||
|
||||
Admin access is necessary for `mkcert` to install the certificate into your
|
||||
trust stores.
|
||||
|
||||
```bash
|
||||
sudo -v
|
||||
```
|
||||
|
||||
```bash
|
||||
bash ./scripts/local-ca
|
||||
```
|
||||
|
||||
### DNS Setup {#DNS}
|
||||
|
||||
Configure your machine to resolve `*.holos.localhost` to your loopback
|
||||
interface. This is necessary for requests to reach the workload cluster.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="macos" label="macOS" default>
|
||||
Cache sudo credentials.
|
||||
|
||||
Admin access is necessary to setup a local dnsmasq instance and configure
|
||||
macOS's DNS resolver.
|
||||
|
||||
```bash
|
||||
sudo -v
|
||||
```
|
||||
|
||||
Resolve *.holos.localhost DNS queries to 127.0.0.1.
|
||||
|
||||
```bash
|
||||
bash ./scripts/local-dns
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="linux" label="Linux">
|
||||
[NSS-myhostname](http://man7.org/linux/man-pages/man8/nss-myhostname.8.html)
|
||||
ships with many Linux distributions and should resolve *.localhost
|
||||
automatically to 127.0.0.1.
|
||||
|
||||
Otherwise it is installable with:
|
||||
|
||||
```bash
|
||||
sudo apt install libnss-myhostname
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="windows" label="Windows">
|
||||
Ensure the loopback interface has at least the following names in `C:\windows\system32\drivers\etc\hosts`
|
||||
|
||||
```
|
||||
127.0.0.1 httpbin.holos.localhost app.holos.localhost
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
## Apply the Platform Components {#Apply-Platform-Components}
|
||||
|
||||
Use `kubectl` to apply each platform component. In production, it's common to
|
||||
fully automate this process with ArgoCD, but we use `kubectl` to the same
|
||||
effect.
|
||||
|
||||
### Service Mesh
|
||||
|
||||
The platform service mesh provides an ingress gateway and connectivity useful
|
||||
for observability, reliability, and security.
|
||||
|
||||
#### Namespaces
|
||||
|
||||
With Holos, components are automatically added to the namespaces component,
|
||||
useful for centrally managed policies.
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f ./deploy/clusters/workload/components/namespaces
|
||||
```
|
||||
|
||||
#### Custom Resource Definitions
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f ./deploy/clusters/workload/components/gateway-api
|
||||
kubectl apply --server-side=true -f ./deploy/clusters/workload/components/istio-base
|
||||
```
|
||||
|
||||
#### Cert Manager {#cert-manager}
|
||||
|
||||
Apply the cert-manager controller.
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f ./deploy/clusters/workload/components/cert-manager
|
||||
```
|
||||
|
||||
Apply the ClusterIssuer which issues Certificate resources using the local
|
||||
certificate authority.
|
||||
|
||||
```bash
|
||||
kubectl -n cert-manager wait pod -l app.kubernetes.io/component=webhook --for=condition=Ready
|
||||
kubectl apply --server-side=true -f deploy/clusters/workload/components/local-ca
|
||||
kubectl apply --server-side=true -f deploy/clusters/workload/components/certificates
|
||||
```
|
||||
|
||||
#### Istio {#Istio}
|
||||
|
||||
Istio implements the Service Mesh.
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f ./deploy/clusters/workload/components/istio-cni
|
||||
kubectl apply --server-side=true -f ./deploy/clusters/workload/components/istiod
|
||||
kubectl apply --server-side=true -f ./deploy/clusters/workload/components/gateway
|
||||
```
|
||||
|
||||
Verify the Gateway is programmed and the listeners have been accepted:
|
||||
|
||||
```bash
|
||||
kubectl -n istio-gateways wait gateway default --for=condition=Accepted
|
||||
```
|
||||
|
||||
#### httpbin {#httpbin}
|
||||
|
||||
httpbin is a simple backend service useful for end-to-end testing.
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f deploy/clusters/workload/components/httpbin-backend
|
||||
kubectl apply --server-side=true -f deploy/clusters/workload/components/httpbin-routes
|
||||
kubectl -n holos-system wait pod -l app.kubernetes.io/instance=httpbin --for=condition=Ready
|
||||
```
|
||||
|
||||
:::important
|
||||
|
||||
Browse to [https://httpbin.holos.localhost/](https://httpbin.holos.localhost/)
|
||||
to verify end to end connectivity. You should see the httpbin index page.
|
||||
|
||||
:::
|
||||
|
||||
### Authenticating Proxy
|
||||
|
||||
The auth proxy is responsible for authenticating browser requests, handling the
|
||||
oidc authentication flow, and providing a signed id token to the rest of the
|
||||
services in the mesh.
|
||||
|
||||
#### Cookie Secret
|
||||
|
||||
The auth proxy stores session information in an encrypted cookie. Generate a
|
||||
random cookie encryption Secret and apply.
|
||||
|
||||
```bash
|
||||
LC_ALL=C tr -dc A-Za-z0-9 </dev/urandom \
|
||||
| head -c 32 \
|
||||
| kubectl create secret generic "authproxy" \
|
||||
--from-file=cookiesecret=/dev/stdin \
|
||||
--dry-run=client -o yaml \
|
||||
| kubectl apply -n istio-gateways -f-
|
||||
```
|
||||
|
||||
#### Deployment
|
||||
|
||||
The auth proxy Deployment receives requests from web browsers and responds with
|
||||
an authentication decision.
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f deploy/clusters/workload/components/authproxy
|
||||
kubectl apply --server-side=true -f deploy/clusters/workload/components/authroutes
|
||||
```
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Signed In">
|
||||
Verify authentication is working by visiting
|
||||
[https://httpbin.holos.localhost/holos/authproxy](https://httpbin.holos.localhost/holos/authproxy).
|
||||
The auth proxy should respond with a simple `Authenticated` response.
|
||||
|
||||
Istio will respond with `no healthy upstream` until the pod becomes ready.
|
||||
Wait for the pod to become ready with:
|
||||
|
||||
```bash
|
||||
kubectl -n holos-system wait pod -l app.kubernetes.io/instance=httpbin --for=condition=Ready
|
||||
```
|
||||
|
||||
Once authenticated, visit
|
||||
[https://httpbin.holos.localhost/holos/authproxy/userinfo](https://httpbin.holos.localhost/holos/authproxy/userinfo)
|
||||
which returns a subset of claims from your id token:
|
||||
|
||||
```json
|
||||
{
|
||||
"user": "275552236589843464",
|
||||
"email": "demo@holos.run",
|
||||
"preferredUsername": "demo"
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Signed Out">
|
||||
The auth proxy will always try to sign you in when you are signed out, so
|
||||
there isn't much to do here. Please do take a moment to glance at the
|
||||
Signed In tab to see how this would work if you were signed in.
|
||||
|
||||
The `k3d` platform relies on `https://login.holos.run` to issue id tokens.
|
||||
Authorization has been configured against fake request headers instead of
|
||||
the real `x-oidc-id-token` header.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Authorization Policy
|
||||
|
||||
Configure authorization policies using attributes of the authenticated request.
|
||||
Authorization policies route web requests through the auth proxy and then
|
||||
validate all requests against the `x-oidc-id-token` header.
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f deploy/clusters/workload/components/authpolicy
|
||||
```
|
||||
|
||||
Istio make take a few seconds to program the Gateway with the
|
||||
AuthorizationPolicy resources.
|
||||
|
||||
## Try out Zero Trust
|
||||
|
||||
A basic Zero Trust security model is now in place. The platform authenticates
|
||||
and authorizes requests before they reach the backend service.
|
||||
|
||||
### Browser
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Signed In">
|
||||
The platform has been configured to authorize requests with a `x-oidc-id-token` header.
|
||||
|
||||
1. Verify authentication is working by browsing to [https://httpbin.holos.localhost/dump/request](https://httpbin.holos.localhost/dump/request).
|
||||
- Refresh the page a few times.
|
||||
- The `httpbin` backend pods should echo back the `x-oidc-id-token`
|
||||
header injected by the auth proxy.
|
||||
2. Note the `x-oidc-id-token` header is not sent by your browser but is
|
||||
received by the backend service.
|
||||
- This design reduces the risk of exposing id tokens in the browser.
|
||||
- Browser request size remains constant as more claims are added to id
|
||||
tokens.
|
||||
- Reliability improves because id tokens often overflow request header
|
||||
buffers when they pass through middle boxes across the internet.
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Signed Out">
|
||||
The platform has been configured to authorize requests with a `User-Agent: anonymous` header.
|
||||
|
||||
1. Open an incognito window (Cmd+Shift+N) to verify the platform is
|
||||
enforcing the authorization policy.
|
||||
2. Browse to
|
||||
[https://httpbin.holos.localhost/dump/request](https://httpbin.holos.localhost/dump/request)
|
||||
you should be redirected to the sign in page by the auth proxy.
|
||||
- You **do not** need to register or sign in.
|
||||
- This step verifies the platform is redirecting unauthenticated
|
||||
requests to the identity provider.
|
||||
- Navigate back or close and re-open an incognito window.
|
||||
3. Set your `User-Agent` header to `anonymous` using your browser developer tools.
|
||||
- For Chrome the process is described
|
||||
[here](https://developer.chrome.com/docs/devtools/device-mode/override-user-agent#override_the_user_agent_string).
|
||||
- The purpose is to simulate an authenticated request.
|
||||
4. Browse to
|
||||
[https://httpbin.holos.localhost/dump/request](https://httpbin.holos.localhost/dump/request).
|
||||
- The platform should allow the request through to the backend pod.
|
||||
- `httpbin` should echo back your request which should contain `User-Agent: anonymous`.
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Command Line
|
||||
|
||||
Verify unauthenticated requests are blocked by default outside the browser.
|
||||
|
||||
```bash
|
||||
curl -I https://httpbin.holos.localhost/dump/request
|
||||
```
|
||||
|
||||
You should receive a `HTTP/2 302` response that redirects to `location:
|
||||
https://login.holos.run` to start the oauth login flow.
|
||||
|
||||
Next, verify authenticated requests are allowed.
|
||||
|
||||
<Tabs groupId="registration">
|
||||
<TabItem value="registered" label="Signed In">
|
||||
The platform is configured to authenticate the id token present in the
|
||||
`x-oidc-id-token` header.
|
||||
|
||||
💡 It also works with `grpcurl`.
|
||||
|
||||
```bash
|
||||
curl -H x-oidc-id-token:$(holos token) https://httpbin.holos.localhost/dump/request
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="unregistered" label="Signed Out">
|
||||
The platform is configured to authorize any request with `User-Agent:
|
||||
anonymous` in place of validating the oidc id token.
|
||||
|
||||
💡 Take a moment to click the Signed In tab, I don't want you to miss how
|
||||
cool `$(holos token)` is.
|
||||
|
||||
```bash
|
||||
curl -A anonymous https://httpbin.holos.localhost/dump/request
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
You should receive a response showing the request headers the backend received.
|
||||
|
||||
:::tip
|
||||
|
||||
Note how the platform secures both web browser and command line api access to
|
||||
the backend httpbin service. httpbin itself has no authentication or
|
||||
authorization functionality.
|
||||
|
||||
:::
|
||||
|
||||
## Summary
|
||||
|
||||
Thank you for taking the time to try out Holos. In this guide, you built the
|
||||
foundation of a software development platform that:
|
||||
|
||||
1. Provides a unified configuration model with CUE that
|
||||
- Supports unmodified Helm Charts, Kustomize Kustomizations, plain YAML.
|
||||
- Provides a web form to pass top level parameters.
|
||||
2. Reduces errors by eliminating the need to template unstructured text.
|
||||
3. Is composable and scales down to a local machine.
|
||||
4. Provides an way to safely configure broad authentication and authorization
|
||||
policy.
|
||||
|
||||
## Next Steps
|
||||
|
||||
Dive deeper with the following resources that build on the foundation you have now.
|
||||
|
||||
1. Explore the [Rendering Process](/docs/design/rendering) in Holos.
|
||||
2. Dive deeper into the [Platform Manifests](./platform-manifests) rendered in this guide.
|
||||
3. Deploy [ArgoCD](/docs/guides/argocd) onto the foundation you built.
|
||||
4. Deploy [Backstage](/docs/guides/backstage) as a portal to the integrated platform components.
|
||||
|
||||
## Clean-Up
|
||||
|
||||
If you'd like to clean up the resources you created in this guide, remove them
|
||||
with:
|
||||
|
||||
```bash
|
||||
k3d cluster delete workload
|
||||
rm -rf holos-k3d
|
||||
```
|
||||
137
doc/md/guides/try-holos/platform-manifests.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Platform Manifests
|
||||
|
||||
This document provides an example of how Holos uses CUE and Helm to unify and
|
||||
render the platform configuration. It refers to the manifests rendered in the
|
||||
[Try Holos Locally](/docs/guides/try-holos/) guide.
|
||||
|
||||
Take a moment to review the manifests `holos` rendered to build the platform.
|
||||
|
||||
### ArgoCD Application
|
||||
|
||||
Note the Git URL in the Platform Model is used to derive the ArgoCD
|
||||
`Application` resource for all of the platform components.
|
||||
|
||||
```yaml
|
||||
# deploy/clusters/workload/gitops/namespaces.application.gen.yaml
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: namespaces
|
||||
namespace: argocd
|
||||
spec:
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
project: default
|
||||
source:
|
||||
# highlight-next-line
|
||||
path: /deploy/clusters/workload/components/namespaces
|
||||
# highlight-next-line
|
||||
repoURL: https://github.com/holos-run/holos-k3d.git
|
||||
# highlight-next-line
|
||||
targetRevision: HEAD
|
||||
```
|
||||
|
||||
One ArgoCD `Application` resource is produced for each Holos component by
|
||||
default. The CUE definition which produces the rendered output is defined in
|
||||
`buildplan.cue` around line 222.
|
||||
|
||||
:::tip
|
||||
|
||||
Note how CUE does not use error-prone text templates, the language is well
|
||||
specified and typed which reduces errors when unifying the configuration with
|
||||
the Platform Model in the following `#Argo` definition.
|
||||
|
||||
:::
|
||||
|
||||
```cue
|
||||
// buildplan.cue
|
||||
|
||||
// #Argo represents an argocd Application resource for each component, written
|
||||
// using the #HolosComponent.deployFiles field.
|
||||
#Argo: {
|
||||
ComponentName: string
|
||||
|
||||
Application: app.#Application & {
|
||||
metadata: name: ComponentName
|
||||
metadata: namespace: "argocd"
|
||||
spec: {
|
||||
destination: server: "https://kubernetes.default.svc"
|
||||
project: "default"
|
||||
source: {
|
||||
// highlight-next-line
|
||||
path: "\(_Platform.Model.argocd.deployRoot)/deploy/clusters/\(_ClusterName)/components/\(ComponentName)"
|
||||
// highlight-next-line
|
||||
repoURL: _Platform.Model.argocd.repoURL
|
||||
// highlight-next-line
|
||||
targetRevision: _Platform.Model.argocd.targetRevision
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deployFiles represents the output files to write along side the component.
|
||||
deployFiles: "clusters/\(_ClusterName)/gitops/\(ComponentName).application.gen.yaml": yaml.Marshal(Application)
|
||||
}
|
||||
```
|
||||
|
||||
### Helm Chart
|
||||
|
||||
The `cert-manger` component renders using the upstream Helm chart. The build
|
||||
plan that defines the helm chart to use along with the values to provide looks
|
||||
like the following.
|
||||
|
||||
:::tip
|
||||
|
||||
Holos fully supports your existing Helm charts. Consider leveraging `holos` as
|
||||
an alternative to umbrella charts.
|
||||
|
||||
:::
|
||||
|
||||
```cue
|
||||
// components/cert-manager/cert-manager.cue
|
||||
package holos
|
||||
|
||||
// Produce a helm chart build plan.
|
||||
(#Helm & Chart).Output
|
||||
|
||||
let Chart = {
|
||||
Name: "cert-manager"
|
||||
Version: "1.14.5"
|
||||
Namespace: "cert-manager"
|
||||
|
||||
Repo: name: "jetstack"
|
||||
Repo: url: "https://charts.jetstack.io"
|
||||
|
||||
// highlight-next-line
|
||||
Values: {
|
||||
installCRDs: true
|
||||
startupapicheck: enabled: false
|
||||
// Must not use kube-system on gke autopilot. GKE Warden blocks access.
|
||||
// highlight-next-line
|
||||
global: leaderElection: namespace: Namespace
|
||||
|
||||
// https://cloud.google.com/kubernetes-engine/docs/concepts/autopilot-resource-requests#min-max-requests
|
||||
resources: requests: {
|
||||
cpu: "250m"
|
||||
memory: "512Mi"
|
||||
"ephemeral-storage": "100Mi"
|
||||
}
|
||||
// highlight-next-line
|
||||
webhook: resources: Values.resources
|
||||
// highlight-next-line
|
||||
cainjector: resources: Values.resources
|
||||
// highlight-next-line
|
||||
startupapicheck: resource: Values.resources
|
||||
|
||||
// https://cloud.google.com/kubernetes-engine/docs/how-to/autopilot-spot-pods
|
||||
nodeSelector: {
|
||||
"kubernetes.io/os": "linux"
|
||||
if _ClusterName == "management" {
|
||||
"cloud.google.com/gke-spot": "true"
|
||||
}
|
||||
}
|
||||
webhook: nodeSelector: Values.nodeSelector
|
||||
cainjector: nodeSelector: Values.nodeSelector
|
||||
startupapicheck: nodeSelector: Values.nodeSelector
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,47 +1,64 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
# Introduction
|
||||
|
||||
# Tutorial Intro
|
||||
⚡️ Holos will help you build your **software development platform in no time.**
|
||||
|
||||
Let's discover **Docusaurus in less than 5 minutes**.
|
||||
💸 Building a software development platform is **time consuming and expensive**. Spend more time building features for your customers and less time managing your development platform.
|
||||
|
||||
## Getting Started
|
||||
💥 Already have a platform? Add new features and services to your platform easily with Holos.
|
||||
|
||||
Get started by **creating a new site**.
|
||||
🧐 Holos is a platform builder. It builds a hollistic software development platform composed of best-of-breed cloud native open source projects. Holos is also a tool to make it easier to manage cloud infrastructure by providing a typed alternative to yaml templates.
|
||||
|
||||
Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**.
|
||||
## Features
|
||||
|
||||
### What you'll need
|
||||
Holos was built to solve two main problems:
|
||||
|
||||
- [Node.js](https://nodejs.org/en/download/) version 18.0 or above:
|
||||
- When installing Node.js, you are recommended to check all checkboxes related to dependencies.
|
||||
1. Building a platform usually takes 3 engineers 6-9 months of effort. Holos provides a reference platform that enables you to deploy and customize your platform in a fraction of the time.
|
||||
2. Configuration changes often cause outages. Existing tools like Helm make it difficult to understand the impact a configuration change will have. Holos provides a unique, unified configuration model powered by CUE that makes it safer and easier to roll out configuration changes.
|
||||
|
||||
## Generate a new site
|
||||
A core principle of Holos is that organizations gain value from owning the the platform they build on. Avoid vendor lock-in, future price hikes, and expensive licensing changes by building on a solid foundation of open source, cloud native computing foundation backed projects.
|
||||
|
||||
Generate a new Docusaurus site using the **classic template**.
|
||||
The following features are built into the Holos reference platform.
|
||||
|
||||
The classic template will automatically be added to your project after you run the command:
|
||||
:::tip
|
||||
|
||||
```bash
|
||||
npm init docusaurus@latest my-website classic
|
||||
```
|
||||
Don't see your preferred technology in the stack? Holos is designed to enable you to swap out components of the platform tech stack.
|
||||
|
||||
You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor.
|
||||
:::
|
||||
|
||||
The command also installs all necessary dependencies you need to run Docusaurus.
|
||||
- **Continuous Delivery**
|
||||
- Holos builds a GitOps workflow for each application running in the platform.
|
||||
- Developers push changes which are automatically deployed.
|
||||
- Powered by [ArgoCD](https://argo-cd.readthedocs.io/en/stable/)
|
||||
- **Identity and Access Management** (IAM)
|
||||
- Holos builds a standard OIDC identity provider for you.
|
||||
- Integrates with your exisitng IAM and SSO system, or works independently.
|
||||
- Powerful customer identity and access management features.
|
||||
- Role based access control.
|
||||
- Powered by [ZITADEL](https://zitadel.com/)
|
||||
- **Zero Trust**
|
||||
- Authenticate and Authorize users at the platform layer instead of or in addition to the application layer.
|
||||
- Integrated with observability to measure and alert about problems before customers complain.
|
||||
- Powered by [Istio](https://istio.io/)
|
||||
- **Observability**
|
||||
- Holos collects performance and availability metrics automatically, without requiring application changes.
|
||||
- Optional, deeper integration into the application layer.
|
||||
- Distributed Tracing
|
||||
- Logging
|
||||
- Powered by Prometheus, Grafana, Loki, and OpenTelemetry.
|
||||
- **Data Platform**
|
||||
- Integrated management of PostgreSQL
|
||||
- Automatic backups
|
||||
- Automatic restore from backup
|
||||
- Quickly fail over across multiple regions
|
||||
- **Multi-Region**
|
||||
- Holos is designed to operate in multiple regions and multiple clouds.
|
||||
- Keep customer data in the region that makes the most sense for your business.
|
||||
- Easily cut over from one region to another for redundancy and business continuity.
|
||||
|
||||
## Start your site
|
||||
## Development Status
|
||||
|
||||
Run the development server:
|
||||
Holos is being actively developed by [Open Infrastructure Services](https://openinfrastructure.co). Release can be found [here](https://github.com/holos-run/holos/releases).
|
||||
|
||||
```bash
|
||||
cd my-website
|
||||
npm run start
|
||||
```
|
||||
## Adoption
|
||||
|
||||
The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there.
|
||||
|
||||
The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/.
|
||||
|
||||
Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes.
|
||||
Organizations who have officially adopted Holos can be found [here](https://github.com/holos-run/holos/blob/main/ADOPTERS.md).
|
||||
|
||||
28
doc/md/local-development.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Local Development
|
||||
|
||||
This document captures notes on locally developing Holos.
|
||||
|
||||
Follow the steps in [Try Holos Locally](/docs/guides/try-holos), but take care
|
||||
to select `Develop` tabs when creating the k3d cluster so you have a local
|
||||
registry to push to.
|
||||
|
||||
## Apply Resources
|
||||
|
||||
Work will be done in the `dev-holos` namespace.
|
||||
|
||||
Apply the infrastructure, which should persist when tilt is started / stopped.
|
||||
|
||||
```bash
|
||||
kubectl apply --server-side=true -f ./hack/tilt/k8s/dev-holos-infra
|
||||
```
|
||||
|
||||
This creates the PostgresCluster, service account, etc...
|
||||
|
||||
## Start tilt
|
||||
|
||||
Tilt will build the go executable, build the container, then push it to the
|
||||
local repository associated with k3d.
|
||||
|
||||
```bash
|
||||
./hack/tilt/bin/tilt up
|
||||
```
|
||||
3
doc/md/reference-platform/architecture.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Architecture
|
||||
|
||||
Coming soon.
|
||||
10
doc/md/runbooks/login/backups.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# ZITADEL Backups
|
||||
|
||||
Refer to [Schedule backups](https://cloudnative-pg.io/documentation/1.23/backup/#scheduled-backups)
|
||||
|
||||
By default ZITADEL is backed up daily to S3. When restoring into a new cluster
|
||||
of the same name, increment the revision variable to create a new blank folder
|
||||
for the new cluster WAL. The cluster will not initialize unless the WAL
|
||||
directory is empty.
|
||||
|
||||
The cluster will recovery from the previous rev.
|
||||
3
doc/md/runbooks/login/failover.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Overview
|
||||
|
||||
TODO: This runbook needs to be updated to reflect the switch from PGO to CNPG.
|
||||
3
doc/md/runbooks/postgres-backup.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Postgres Full Backup
|
||||
|
||||
Refer to [On-demand backups](https://cloudnative-pg.io/documentation/1.23/backup/#on-demand-backups)
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"label": "Tutorial - Basics",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "5 minutes to learn the most important Docusaurus concepts."
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# Congratulations!
|
||||
|
||||
You have just learned the **basics of Docusaurus** and made some changes to the **initial template**.
|
||||
|
||||
Docusaurus has **much more to offer**!
|
||||
|
||||
Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**.
|
||||
|
||||
Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610)
|
||||
|
||||
## What's next?
|
||||
|
||||
- Read the [official documentation](https://docusaurus.io/)
|
||||
- Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config)
|
||||
- Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration)
|
||||
- Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout)
|
||||
- Add a [search bar](https://docusaurus.io/docs/search)
|
||||
- Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase)
|
||||
- Get involved in the [Docusaurus Community](https://docusaurus.io/community/support)
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Create a Blog Post
|
||||
|
||||
Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed...
|
||||
|
||||
## Create your first Post
|
||||
|
||||
Create a file at `blog/2021-02-28-greetings.md`:
|
||||
|
||||
```md title="blog/2021-02-28-greetings.md"
|
||||
---
|
||||
slug: greetings
|
||||
title: Greetings!
|
||||
authors:
|
||||
- name: Joel Marcey
|
||||
title: Co-creator of Docusaurus 1
|
||||
url: https://github.com/JoelMarcey
|
||||
image_url: https://github.com/JoelMarcey.png
|
||||
- name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
tags: [greetings]
|
||||
---
|
||||
|
||||
Congratulations, you have made your first post!
|
||||
|
||||
Feel free to play around and edit this post as much as you like.
|
||||
```
|
||||
|
||||
A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings).
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Create a Document
|
||||
|
||||
Documents are **groups of pages** connected through:
|
||||
|
||||
- a **sidebar**
|
||||
- **previous/next navigation**
|
||||
- **versioning**
|
||||
|
||||
## Create your first Doc
|
||||
|
||||
Create a Markdown file at `docs/hello.md`:
|
||||
|
||||
```md title="docs/hello.md"
|
||||
# Hello
|
||||
|
||||
This is my **first Docusaurus document**!
|
||||
```
|
||||
|
||||
A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello).
|
||||
|
||||
## Configure the Sidebar
|
||||
|
||||
Docusaurus automatically **creates a sidebar** from the `docs` folder.
|
||||
|
||||
Add metadata to customize the sidebar label and position:
|
||||
|
||||
```md title="docs/hello.md" {1-4}
|
||||
---
|
||||
sidebar_label: 'Hi!'
|
||||
sidebar_position: 3
|
||||
---
|
||||
|
||||
# Hello
|
||||
|
||||
This is my **first Docusaurus document**!
|
||||
```
|
||||
|
||||
It is also possible to create your sidebar explicitly in `sidebars.js`:
|
||||
|
||||
```js title="sidebars.js"
|
||||
export default {
|
||||
tutorialSidebar: [
|
||||
'intro',
|
||||
// highlight-next-line
|
||||
'hello',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Tutorial',
|
||||
items: ['tutorial-basics/create-a-document'],
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
@@ -1,43 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Create a Page
|
||||
|
||||
Add **Markdown or React** files to `src/pages` to create a **standalone page**:
|
||||
|
||||
- `src/pages/index.js` → `localhost:3000/`
|
||||
- `src/pages/foo.md` → `localhost:3000/foo`
|
||||
- `src/pages/foo/bar.js` → `localhost:3000/foo/bar`
|
||||
|
||||
## Create your first React Page
|
||||
|
||||
Create a file at `src/pages/my-react-page.js`:
|
||||
|
||||
```jsx title="src/pages/my-react-page.js"
|
||||
import React from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
|
||||
export default function MyReactPage() {
|
||||
return (
|
||||
<Layout>
|
||||
<h1>My React page</h1>
|
||||
<p>This is a React page</p>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page).
|
||||
|
||||
## Create your first Markdown Page
|
||||
|
||||
Create a file at `src/pages/my-markdown-page.md`:
|
||||
|
||||
```mdx title="src/pages/my-markdown-page.md"
|
||||
# My Markdown page
|
||||
|
||||
This is a Markdown page
|
||||
```
|
||||
|
||||
A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page).
|
||||
@@ -1,31 +0,0 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# Deploy your site
|
||||
|
||||
Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**).
|
||||
|
||||
It builds your site as simple **static HTML, JavaScript and CSS files**.
|
||||
|
||||
## Build your site
|
||||
|
||||
Build your site **for production**:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
The static files are generated in the `build` folder.
|
||||
|
||||
## Deploy your site
|
||||
|
||||
Test your production build locally:
|
||||
|
||||
```bash
|
||||
npm run serve
|
||||
```
|
||||
|
||||
The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/).
|
||||
|
||||
You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**).
|
||||
@@ -1,152 +0,0 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# Markdown Features
|
||||
|
||||
Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**.
|
||||
|
||||
## Front Matter
|
||||
|
||||
Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/):
|
||||
|
||||
```text title="my-doc.md"
|
||||
// highlight-start
|
||||
---
|
||||
id: my-doc-id
|
||||
title: My document title
|
||||
description: My document description
|
||||
slug: /my-custom-url
|
||||
---
|
||||
// highlight-end
|
||||
|
||||
## Markdown heading
|
||||
|
||||
Markdown text with [links](./hello.md)
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
Regular Markdown links are supported, using url paths or relative file paths.
|
||||
|
||||
```md
|
||||
Let's see how to [Create a page](/create-a-page).
|
||||
```
|
||||
|
||||
```md
|
||||
Let's see how to [Create a page](./create-a-page.md).
|
||||
```
|
||||
|
||||
**Result:** Let's see how to [Create a page](./create-a-page.md).
|
||||
|
||||
## Images
|
||||
|
||||
Regular Markdown images are supported.
|
||||
|
||||
You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`):
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||

|
||||
|
||||
You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them:
|
||||
|
||||
```md
|
||||

|
||||
```
|
||||
|
||||
## Code Blocks
|
||||
|
||||
Markdown code blocks are supported with Syntax highlighting.
|
||||
|
||||
````md
|
||||
```jsx title="src/components/HelloDocusaurus.js"
|
||||
function HelloDocusaurus() {
|
||||
return <h1>Hello, Docusaurus!</h1>;
|
||||
}
|
||||
```
|
||||
````
|
||||
|
||||
```jsx title="src/components/HelloDocusaurus.js"
|
||||
function HelloDocusaurus() {
|
||||
return <h1>Hello, Docusaurus!</h1>;
|
||||
}
|
||||
```
|
||||
|
||||
## Admonitions
|
||||
|
||||
Docusaurus has a special syntax to create admonitions and callouts:
|
||||
|
||||
```md
|
||||
:::tip My tip
|
||||
|
||||
Use this awesome feature option
|
||||
|
||||
:::
|
||||
|
||||
:::danger Take care
|
||||
|
||||
This action is dangerous
|
||||
|
||||
:::
|
||||
```
|
||||
|
||||
:::tip My tip
|
||||
|
||||
Use this awesome feature option
|
||||
|
||||
:::
|
||||
|
||||
:::danger Take care
|
||||
|
||||
This action is dangerous
|
||||
|
||||
:::
|
||||
|
||||
## MDX and React Components
|
||||
|
||||
[MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**:
|
||||
|
||||
```jsx
|
||||
export const Highlight = ({children, color}) => (
|
||||
<span
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
borderRadius: '20px',
|
||||
color: '#fff',
|
||||
padding: '10px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => {
|
||||
alert(`You clicked the color ${color} with label ${children}`)
|
||||
}}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
|
||||
|
||||
This is <Highlight color="#1877F2">Facebook blue</Highlight> !
|
||||
```
|
||||
|
||||
export const Highlight = ({children, color}) => (
|
||||
<span
|
||||
style={{
|
||||
backgroundColor: color,
|
||||
borderRadius: '20px',
|
||||
color: '#fff',
|
||||
padding: '10px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
onClick={() => {
|
||||
alert(`You clicked the color ${color} with label ${children}`);
|
||||
}}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
|
||||
This is <Highlight color="#25c2a0">Docusaurus green</Highlight> !
|
||||
|
||||
This is <Highlight color="#1877F2">Facebook blue</Highlight> !
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"label": "Tutorial - Extras",
|
||||
"position": 3,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 27 KiB |
@@ -1,55 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Manage Docs Versions
|
||||
|
||||
Docusaurus can manage multiple versions of your docs.
|
||||
|
||||
## Create a docs version
|
||||
|
||||
Release a version 1.0 of your project:
|
||||
|
||||
```bash
|
||||
npm run docusaurus docs:version 1.0
|
||||
```
|
||||
|
||||
The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created.
|
||||
|
||||
Your docs now have 2 versions:
|
||||
|
||||
- `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs
|
||||
- `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs**
|
||||
|
||||
## Add a Version Dropdown
|
||||
|
||||
To navigate seamlessly across versions, add a version dropdown.
|
||||
|
||||
Modify the `docusaurus.config.js` file:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
items: [
|
||||
// highlight-start
|
||||
{
|
||||
type: 'docsVersionDropdown',
|
||||
},
|
||||
// highlight-end
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The docs version dropdown appears in your navbar:
|
||||
|
||||

|
||||
|
||||
## Update an existing version
|
||||
|
||||
It is possible to edit versioned docs in their respective folder:
|
||||
|
||||
- `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello`
|
||||
- `docs/hello.md` updates `http://localhost:3000/docs/next/hello`
|
||||
@@ -1,88 +0,0 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# Translate your site
|
||||
|
||||
Let's translate `docs/intro.md` to French.
|
||||
|
||||
## Configure i18n
|
||||
|
||||
Modify `docusaurus.config.js` to add support for the `fr` locale:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'fr'],
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
## Translate a doc
|
||||
|
||||
Copy the `docs/intro.md` file to the `i18n/fr` folder:
|
||||
|
||||
```bash
|
||||
mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/
|
||||
|
||||
cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md
|
||||
```
|
||||
|
||||
Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French.
|
||||
|
||||
## Start your localized site
|
||||
|
||||
Start your site on the French locale:
|
||||
|
||||
```bash
|
||||
npm run start -- --locale fr
|
||||
```
|
||||
|
||||
Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
|
||||
|
||||
:::caution
|
||||
|
||||
In development, you can only use one locale at a time.
|
||||
|
||||
:::
|
||||
|
||||
## Add a Locale Dropdown
|
||||
|
||||
To navigate seamlessly across languages, add a locale dropdown.
|
||||
|
||||
Modify the `docusaurus.config.js` file:
|
||||
|
||||
```js title="docusaurus.config.js"
|
||||
export default {
|
||||
themeConfig: {
|
||||
navbar: {
|
||||
items: [
|
||||
// highlight-start
|
||||
{
|
||||
type: 'localeDropdown',
|
||||
},
|
||||
// highlight-end
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
The locale dropdown now appears in your navbar:
|
||||
|
||||

|
||||
|
||||
## Build your localized site
|
||||
|
||||
Build your site for a specific locale:
|
||||
|
||||
```bash
|
||||
npm run build -- --locale fr
|
||||
```
|
||||
|
||||
Or build your site to include all the locales at once:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
3
doc/website/.gitignore
vendored
@@ -1,6 +1,9 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Build
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
@@ -1,41 +1,34 @@
|
||||
# Website
|
||||
|
||||
This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator.
|
||||
This website is built using [Docusaurus](https://docusaurus.io/), a modern
|
||||
static website generator.
|
||||
|
||||
### Installation
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ yarn
|
||||
```shell
|
||||
npm install
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
```
|
||||
$ yarn start
|
||||
```shell
|
||||
npm run start
|
||||
```
|
||||
|
||||
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||
This command starts a local development server and opens up a browser window.
|
||||
Most changes are reflected live without having to restart the server.
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ yarn build
|
||||
```shell
|
||||
npm run build
|
||||
```
|
||||
|
||||
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
||||
This command generates static content into the `build` directory and can be
|
||||
served using any static contents hosting service.
|
||||
|
||||
### Deployment
|
||||
|
||||
Using SSH:
|
||||
|
||||
```
|
||||
$ USE_SSH=true yarn deploy
|
||||
```
|
||||
|
||||
Not using SSH:
|
||||
|
||||
```
|
||||
$ GIT_USER=<Your GitHub username> yarn deploy
|
||||
```
|
||||
|
||||
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
|
||||
Deployments are made with Cloudflare Pages. Cloudflare deploys on changes to
|
||||
the main branch, and Pull Requests get comments with links to preview
|
||||
environments.
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
---
|
||||
slug: first-blog-post
|
||||
title: First Blog Post
|
||||
authors:
|
||||
name: Gao Wei
|
||||
title: Docusaurus Core Team
|
||||
url: https://github.com/wgao19
|
||||
image_url: https://github.com/wgao19.png
|
||||
tags: [hola, docusaurus]
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
slug: long-blog-post
|
||||
title: Long Blog Post
|
||||
authors: endi
|
||||
tags: [hello, docusaurus]
|
||||
---
|
||||
|
||||
This is the summary of a very long blog post,
|
||||
|
||||
Use a `<!--` `truncate` `-->` comment to limit blog post size in the list view.
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
slug: mdx-blog-post
|
||||
title: MDX Blog Post
|
||||
authors: [slorber]
|
||||
tags: [docusaurus]
|
||||
---
|
||||
|
||||
Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/).
|
||||
|
||||
:::tip
|
||||
|
||||
Use the power of React to create interactive blog posts.
|
||||
|
||||
```js
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
```
|
||||
|
||||
<button onClick={() => alert('button clicked!')}>Click me!</button>
|
||||
|
||||
:::
|
||||
|
Before Width: | Height: | Size: 94 KiB |
@@ -1,25 +0,0 @@
|
||||
---
|
||||
slug: welcome
|
||||
title: Welcome
|
||||
authors: [slorber, yangshun]
|
||||
tags: [facebook, hello, docusaurus]
|
||||
---
|
||||
|
||||
[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog).
|
||||
|
||||
Simply add Markdown files (or folders) to the `blog` directory.
|
||||
|
||||
Regular blog authors can be added to `authors.yml`.
|
||||
|
||||
The blog post date can be extracted from filenames, such as:
|
||||
|
||||
- `2019-05-30-welcome.md`
|
||||
- `2019-05-30-welcome/index.md`
|
||||
|
||||
A blog post folder can be convenient to co-locate blog post images:
|
||||
|
||||

|
||||
|
||||
The blog supports tags as well!
|
||||
|
||||
**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config.
|
||||
8
doc/website/blog/2024-07-03-welcome/index.md
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
slug: welcome
|
||||
title: Welcome
|
||||
authors: [jeff]
|
||||
tags: [holos]
|
||||
---
|
||||
|
||||
TODO - Coming Soon
|
||||
@@ -1,17 +1,17 @@
|
||||
endi:
|
||||
name: Endilie Yacop Sucipto
|
||||
title: Maintainer of Docusaurus
|
||||
url: https://github.com/endiliey
|
||||
image_url: https://github.com/endiliey.png
|
||||
jeff:
|
||||
name: Jeff McCune
|
||||
title: Holos maintainer & creator
|
||||
url: https://github.com/jeffmccune
|
||||
image_url: https://github.com/jeffmccune.png
|
||||
|
||||
yangshun:
|
||||
name: Yangshun Tay
|
||||
title: Front End Engineer @ Facebook
|
||||
url: https://github.com/yangshun
|
||||
image_url: https://github.com/yangshun.png
|
||||
gary:
|
||||
name: Gary Larizza
|
||||
title: Holos maintainer
|
||||
url: https://github.com/glarizza
|
||||
image_url: https://github.com/glarizza.png
|
||||
|
||||
slorber:
|
||||
name: Sébastien Lorber
|
||||
title: Docusaurus maintainer
|
||||
url: https://sebastienlorber.com
|
||||
image_url: https://github.com/slorber.png
|
||||
nate:
|
||||
name: Nate McCurdy
|
||||
title: Holos maintainer
|
||||
url: https://github.com/natemccurdy
|
||||
image_url: https://github.com/natemccurdy.png
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
facebook:
|
||||
label: Facebook
|
||||
permalink: /facebook
|
||||
description: Facebook tag description
|
||||
hello:
|
||||
label: Hello
|
||||
permalink: /hello
|
||||
description: Hello tag description
|
||||
docusaurus:
|
||||
label: Docusaurus
|
||||
permalink: /docusaurus
|
||||
description: Docusaurus tag description
|
||||
hola:
|
||||
label: Hola
|
||||
permalink: /hola
|
||||
description: Hola tag description
|
||||
holos:
|
||||
label: Holos
|
||||
permalink: /holos
|
||||
description: Holos Platform
|
||||
|
||||
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 27 KiB |
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[849],{6164:a=>{a.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Tutorial Intro","href":"/docs/intro","docId":"intro","unlisted":false},{"type":"category","label":"Tutorial - Basics","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Create a Page","href":"/docs/tutorial-basics/create-a-page","docId":"tutorial-basics/create-a-page","unlisted":false},{"type":"link","label":"Create a Document","href":"/docs/tutorial-basics/create-a-document","docId":"tutorial-basics/create-a-document","unlisted":false},{"type":"link","label":"Create a Blog Post","href":"/docs/tutorial-basics/create-a-blog-post","docId":"tutorial-basics/create-a-blog-post","unlisted":false},{"type":"link","label":"Markdown Features","href":"/docs/tutorial-basics/markdown-features","docId":"tutorial-basics/markdown-features","unlisted":false},{"type":"link","label":"Deploy your site","href":"/docs/tutorial-basics/deploy-your-site","docId":"tutorial-basics/deploy-your-site","unlisted":false},{"type":"link","label":"Congratulations!","href":"/docs/tutorial-basics/congratulations","docId":"tutorial-basics/congratulations","unlisted":false}],"href":"/docs/category/tutorial---basics"},{"type":"category","label":"Tutorial - Extras","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Manage Docs Versions","href":"/docs/tutorial-extras/manage-docs-versions","docId":"tutorial-extras/manage-docs-versions","unlisted":false},{"type":"link","label":"Translate your site","href":"/docs/tutorial-extras/translate-your-site","docId":"tutorial-extras/translate-your-site","unlisted":false}],"href":"/docs/category/tutorial---extras"}]},"docs":{"intro":{"id":"intro","title":"Tutorial Intro","description":"Let\'s discover Docusaurus in less than 5 minutes.","sidebar":"tutorialSidebar"},"tutorial-basics/congratulations":{"id":"tutorial-basics/congratulations","title":"Congratulations!","description":"You have just learned the basics of Docusaurus and made some changes to the initial template.","sidebar":"tutorialSidebar"},"tutorial-basics/create-a-blog-post":{"id":"tutorial-basics/create-a-blog-post","title":"Create a Blog Post","description":"Docusaurus creates a page for each blog post, but also a blog index page, a tag system, an RSS feed...","sidebar":"tutorialSidebar"},"tutorial-basics/create-a-document":{"id":"tutorial-basics/create-a-document","title":"Create a Document","description":"Documents are groups of pages connected through:","sidebar":"tutorialSidebar"},"tutorial-basics/create-a-page":{"id":"tutorial-basics/create-a-page","title":"Create a Page","description":"Add Markdown or React files to src/pages to create a standalone page:","sidebar":"tutorialSidebar"},"tutorial-basics/deploy-your-site":{"id":"tutorial-basics/deploy-your-site","title":"Deploy your site","description":"Docusaurus is a static-site-generator (also called Jamstack).","sidebar":"tutorialSidebar"},"tutorial-basics/markdown-features":{"id":"tutorial-basics/markdown-features","title":"Markdown Features","description":"Docusaurus supports Markdown and a few additional features.","sidebar":"tutorialSidebar"},"tutorial-extras/manage-docs-versions":{"id":"tutorial-extras/manage-docs-versions","title":"Manage Docs Versions","description":"Docusaurus can manage multiple versions of your docs.","sidebar":"tutorialSidebar"},"tutorial-extras/translate-your-site":{"id":"tutorial-extras/translate-your-site","title":"Translate your site","description":"Let\'s translate docs/intro.md to French.","sidebar":"tutorialSidebar"}}}}')}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8209],{6535:(e,s,t)=>{t.d(s,{A:()=>v});var a=t(6540),i=t(4164),r=t(781),l=t(4581),n=t(8774),c=t(1312),o=t(6347),d=t(9169);function m(e){const{pathname:s}=(0,o.zy)();return(0,a.useMemo)((()=>e.filter((e=>function(e,s){return!(e.unlisted&&!(0,d.ys)(e.permalink,s))}(e,s)))),[e,s])}const u={sidebar:"sidebar_re4s",sidebarItemTitle:"sidebarItemTitle_pO2u",sidebarItemList:"sidebarItemList_Yudw",sidebarItem:"sidebarItem__DBe",sidebarItemLink:"sidebarItemLink_mo7H",sidebarItemLinkActive:"sidebarItemLinkActive_I1ZP"};var b=t(4848);function g(e){let{sidebar:s}=e;const t=m(s.items);return(0,b.jsx)("aside",{className:"col col--3",children:(0,b.jsxs)("nav",{className:(0,i.A)(u.sidebar,"thin-scrollbar"),"aria-label":(0,c.T)({id:"theme.blog.sidebar.navAriaLabel",message:"Blog recent posts navigation",description:"The ARIA label for recent posts in the blog sidebar"}),children:[(0,b.jsx)("div",{className:(0,i.A)(u.sidebarItemTitle,"margin-bottom--md"),children:s.title}),(0,b.jsx)("ul",{className:(0,i.A)(u.sidebarItemList,"clean-list"),children:t.map((e=>(0,b.jsx)("li",{className:u.sidebarItem,children:(0,b.jsx)(n.A,{isNavLink:!0,to:e.permalink,className:u.sidebarItemLink,activeClassName:u.sidebarItemLinkActive,children:e.title})},e.permalink)))})]})})}var h=t(5600);function j(e){let{sidebar:s}=e;const t=m(s.items);return(0,b.jsx)("ul",{className:"menu__list",children:t.map((e=>(0,b.jsx)("li",{className:"menu__list-item",children:(0,b.jsx)(n.A,{isNavLink:!0,to:e.permalink,className:"menu__link",activeClassName:"menu__link--active",children:e.title})},e.permalink)))})}function p(e){return(0,b.jsx)(h.GX,{component:j,props:e})}function x(e){let{sidebar:s}=e;const t=(0,l.l)();return s?.items.length?"mobile"===t?(0,b.jsx)(p,{sidebar:s}):(0,b.jsx)(g,{sidebar:s}):null}function v(e){const{sidebar:s,toc:t,children:a,...l}=e,n=s&&s.items.length>0;return(0,b.jsx)(r.A,{...l,children:(0,b.jsx)("div",{className:"container margin-vert--lg",children:(0,b.jsxs)("div",{className:"row",children:[(0,b.jsx)(x,{sidebar:s}),(0,b.jsx)("main",{className:(0,i.A)("col",{"col--7":n,"col--9 col--offset-1":!n}),children:a}),t&&(0,b.jsx)("div",{className:"col col--2",children:t})]})})})}},9158:(e,s,t)=>{t.r(s),t.d(s,{default:()=>j});t(6540);var a=t(4164),i=t(1312);const r=()=>(0,i.T)({id:"theme.tags.tagsPageTitle",message:"Tags",description:"The title of the tag list page"});var l=t(1213),n=t(7559),c=t(6535),o=t(6133),d=t(1107);const m={tag:"tag_Nnez"};var u=t(4848);function b(e){let{letterEntry:s}=e;return(0,u.jsxs)("article",{children:[(0,u.jsx)(d.A,{as:"h2",id:s.letter,children:s.letter}),(0,u.jsx)("ul",{className:"padding--none",children:s.tags.map((e=>(0,u.jsx)("li",{className:m.tag,children:(0,u.jsx)(o.A,{...e})},e.permalink)))}),(0,u.jsx)("hr",{})]})}function g(e){let{tags:s}=e;const t=function(e){const s={};return Object.values(e).forEach((e=>{const t=function(e){return e[0].toUpperCase()}(e.label);s[t]??=[],s[t].push(e)})),Object.entries(s).sort(((e,s)=>{let[t]=e,[a]=s;return t.localeCompare(a)})).map((e=>{let[s,t]=e;return{letter:s,tags:t.sort(((e,s)=>e.label.localeCompare(s.label)))}}))}(s);return(0,u.jsx)("section",{className:"margin-vert--lg",children:t.map((e=>(0,u.jsx)(b,{letterEntry:e},e.letter)))})}var h=t(1463);function j(e){let{tags:s,sidebar:t}=e;const i=r();return(0,u.jsxs)(l.e3,{className:(0,a.A)(n.G.wrapper.blogPages,n.G.page.blogTagsListPage),children:[(0,u.jsx)(l.be,{title:i}),(0,u.jsx)(h.A,{tag:"blog_tags_list"}),(0,u.jsxs)(c.A,{sidebar:t,children:[(0,u.jsx)(d.A,{as:"h1",children:i}),(0,u.jsx)(g,{tags:s})]})]})}},6133:(e,s,t)=>{t.d(s,{A:()=>n});t(6540);var a=t(4164),i=t(8774);const r={tag:"tag_zVej",tagRegular:"tagRegular_sFm0",tagWithCount:"tagWithCount_h2kH"};var l=t(4848);function n(e){let{permalink:s,label:t,count:n,description:c}=e;return(0,l.jsxs)(i.A,{href:s,title:c,className:(0,a.A)(r.tag,n?r.tagWithCount:r.tagRegular),children:[t,n&&(0,l.jsx)("span",{children:n})]})}}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2237],{3363:(e,t,i)=>{i.d(t,{A:()=>a});i(6540);var n=i(4164),s=i(1312),o=i(1107),r=i(4848);function a(e){let{className:t}=e;return(0,r.jsx)("main",{className:(0,n.A)("container margin-vert--xl",t),children:(0,r.jsx)("div",{className:"row",children:(0,r.jsxs)("div",{className:"col col--6 col--offset-3",children:[(0,r.jsx)(o.A,{as:"h1",className:"hero__title",children:(0,r.jsx)(s.A,{id:"theme.NotFound.title",description:"The title of the 404 page",children:"Page Not Found"})}),(0,r.jsx)("p",{children:(0,r.jsx)(s.A,{id:"theme.NotFound.p1",description:"The first paragraph of the 404 page",children:"We could not find what you were looking for."})}),(0,r.jsx)("p",{children:(0,r.jsx)(s.A,{id:"theme.NotFound.p2",description:"The 2nd paragraph of the 404 page",children:"Please contact the owner of the site that linked you to the original URL and let them know their link is broken."})})]})})})}},2237:(e,t,i)=>{i.r(t),i.d(t,{default:()=>l});i(6540);var n=i(1312),s=i(1213),o=i(781),r=i(3363),a=i(4848);function l(){const e=(0,n.T)({id:"theme.NotFound.title",message:"Page Not Found"});return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.be,{title:e}),(0,a.jsx)(o.A,{children:(0,a.jsx)(r.A,{})})]})}}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[2118],{8764:(e,a,t)=>{t.r(a),t.d(a,{assets:()=>i,contentTitle:()=>o,default:()=>p,frontMatter:()=>n,metadata:()=>c,toc:()=>l});var r=t(4848),s=t(8453);const n={sidebar_position:1},o="Create a Page",c={id:"tutorial-basics/create-a-page",title:"Create a Page",description:"Add Markdown or React files to src/pages to create a standalone page:",source:"@site/../md/tutorial-basics/create-a-page.md",sourceDirName:"tutorial-basics",slug:"/tutorial-basics/create-a-page",permalink:"/docs/tutorial-basics/create-a-page",draft:!1,unlisted:!1,editUrl:"https://github.com/holos-run/holos/edit/main/doc/md/../md/tutorial-basics/create-a-page.md",tags:[],version:"current",lastUpdatedBy:"Jeff McCune",lastUpdatedAt:1719897028e3,sidebarPosition:1,frontMatter:{sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Tutorial - Basics",permalink:"/docs/category/tutorial---basics"},next:{title:"Create a Document",permalink:"/docs/tutorial-basics/create-a-document"}},i={},l=[{value:"Create your first React Page",id:"create-your-first-react-page",level:2},{value:"Create your first Markdown Page",id:"create-your-first-markdown-page",level:2}];function d(e){const a={a:"a",code:"code",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(a.h1,{id:"create-a-page",children:"Create a Page"}),"\n",(0,r.jsxs)(a.p,{children:["Add ",(0,r.jsx)(a.strong,{children:"Markdown or React"})," files to ",(0,r.jsx)(a.code,{children:"src/pages"})," to create a ",(0,r.jsx)(a.strong,{children:"standalone page"}),":"]}),"\n",(0,r.jsxs)(a.ul,{children:["\n",(0,r.jsxs)(a.li,{children:[(0,r.jsx)(a.code,{children:"src/pages/index.js"})," \u2192 ",(0,r.jsx)(a.code,{children:"localhost:3000/"})]}),"\n",(0,r.jsxs)(a.li,{children:[(0,r.jsx)(a.code,{children:"src/pages/foo.md"})," \u2192 ",(0,r.jsx)(a.code,{children:"localhost:3000/foo"})]}),"\n",(0,r.jsxs)(a.li,{children:[(0,r.jsx)(a.code,{children:"src/pages/foo/bar.js"})," \u2192 ",(0,r.jsx)(a.code,{children:"localhost:3000/foo/bar"})]}),"\n"]}),"\n",(0,r.jsx)(a.h2,{id:"create-your-first-react-page",children:"Create your first React Page"}),"\n",(0,r.jsxs)(a.p,{children:["Create a file at ",(0,r.jsx)(a.code,{children:"src/pages/my-react-page.js"}),":"]}),"\n",(0,r.jsx)(a.pre,{children:(0,r.jsx)(a.code,{className:"language-jsx",metastring:'title="src/pages/my-react-page.js"',children:"import React from 'react';\nimport Layout from '@theme/Layout';\n\nexport default function MyReactPage() {\n return (\n <Layout>\n <h1>My React page</h1>\n <p>This is a React page</p>\n </Layout>\n );\n}\n"})}),"\n",(0,r.jsxs)(a.p,{children:["A new page is now available at ",(0,r.jsx)(a.a,{href:"http://localhost:3000/my-react-page",children:"http://localhost:3000/my-react-page"}),"."]}),"\n",(0,r.jsx)(a.h2,{id:"create-your-first-markdown-page",children:"Create your first Markdown Page"}),"\n",(0,r.jsxs)(a.p,{children:["Create a file at ",(0,r.jsx)(a.code,{children:"src/pages/my-markdown-page.md"}),":"]}),"\n",(0,r.jsx)(a.pre,{children:(0,r.jsx)(a.code,{className:"language-mdx",metastring:'title="src/pages/my-markdown-page.md"',children:"# My Markdown page\n\nThis is a Markdown page\n"})}),"\n",(0,r.jsxs)(a.p,{children:["A new page is now available at ",(0,r.jsx)(a.a,{href:"http://localhost:3000/my-markdown-page",children:"http://localhost:3000/my-markdown-page"}),"."]})]})}function p(e={}){const{wrapper:a}={...(0,s.R)(),...e.components};return a?(0,r.jsx)(a,{...e,children:(0,r.jsx)(d,{...e})}):d(e)}},8453:(e,a,t)=>{t.d(a,{R:()=>o,x:()=>c});var r=t(6540);const s={},n=r.createContext(s);function o(e){const a=r.useContext(n);return r.useMemo((function(){return"function"==typeof e?e(a):{...a,...e}}),[a,e])}function c(e){let a;return a=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),r.createElement(n.Provider,{value:a},e.children)}}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8462],{8174:s=>{s.exports=JSON.parse('{"tag":{"label":"Docusaurus","permalink":"/blog/tags/docusaurus","description":"Docusaurus tag description","allTagsPath":"/blog/tags","count":4,"unlisted":false},"listMetadata":{"permalink":"/blog/tags/docusaurus","page":1,"postsPerPage":10,"totalPages":1,"totalCount":4,"blogDescription":"Blog","blogTitle":"Blog"}}')}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9858],{5516:e=>{e.exports=JSON.parse('{"name":"docusaurus-plugin-content-blog","id":"default"}')}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[4134],{633:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>s,default:()=>l,frontMatter:()=>r,metadata:()=>p,toc:()=>i});var o=n(4848),a=n(8453);const r={title:"Markdown page example"},s="Markdown page example",p={type:"mdx",permalink:"/markdown-page",source:"@site/src/pages/markdown-page.md",title:"Markdown page example",description:"You don't need React to write simple standalone pages.",frontMatter:{title:"Markdown page example"},unlisted:!1},c={},i=[];function d(e){const t={h1:"h1",p:"p",...(0,a.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t.h1,{id:"markdown-page-example",children:"Markdown page example"}),"\n",(0,o.jsx)(t.p,{children:"You don't need React to write simple standalone pages."})]})}function l(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>s,x:()=>p});var o=n(6540);const a={},r=o.createContext(a);function s(e){const t=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function p(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),o.createElement(r.Provider,{value:t},e.children)}}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8121],{8070:e=>{e.exports=JSON.parse('{"tags":[{"label":"Facebook","permalink":"/blog/tags/facebook","description":"Facebook tag description","count":1},{"label":"Hello","permalink":"/blog/tags/hello","description":"Hello tag description","count":2},{"label":"Docusaurus","permalink":"/blog/tags/docusaurus","description":"Docusaurus tag description","count":4},{"label":"Hola","permalink":"/blog/tags/hola","description":"Hola tag description","count":1}]}')}}]);
|
||||
@@ -1 +0,0 @@
|
||||
"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[9325],{1180:(t,e,o)=>{o.r(e),o.d(e,{assets:()=>l,contentTitle:()=>a,default:()=>p,frontMatter:()=>r,metadata:()=>c,toc:()=>u});var s=o(4848),n=o(8453);const r={slug:"mdx-blog-post",title:"MDX Blog Post",authors:["slorber"],tags:["docusaurus"]},a=void 0,c={permalink:"/blog/mdx-blog-post",source:"@site/blog/2021-08-01-mdx-blog-post.mdx",title:"MDX Blog Post",description:"Blog posts support Docusaurus Markdown features, such as MDX.",date:"2021-08-01T00:00:00.000Z",tags:[{inline:!1,label:"Docusaurus",permalink:"/blog/tags/docusaurus",description:"Docusaurus tag description"}],hasTruncateMarker:!1,authors:[{name:"S\xe9bastien Lorber",title:"Docusaurus maintainer",url:"https://sebastienlorber.com",imageURL:"https://github.com/slorber.png",key:"slorber"}],frontMatter:{slug:"mdx-blog-post",title:"MDX Blog Post",authors:["slorber"],tags:["docusaurus"]},unlisted:!1,prevItem:{title:"Welcome",permalink:"/blog/welcome"},nextItem:{title:"Long Blog Post",permalink:"/blog/long-blog-post"}},l={authorsImageUrls:[void 0]},u=[];function i(t){const e={a:"a",admonition:"admonition",code:"code",p:"p",pre:"pre",...(0,n.R)(),...t.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(e.p,{children:["Blog posts support ",(0,s.jsx)(e.a,{href:"https://docusaurus.io/docs/markdown-features",children:"Docusaurus Markdown features"}),", such as ",(0,s.jsx)(e.a,{href:"https://mdxjs.com/",children:"MDX"}),"."]}),"\n",(0,s.jsxs)(e.admonition,{type:"tip",children:[(0,s.jsx)(e.p,{children:"Use the power of React to create interactive blog posts."}),(0,s.jsx)(e.pre,{children:(0,s.jsx)(e.code,{className:"language-js",children:"<button onClick={() => alert('button clicked!')}>Click me!</button>\n"})}),(0,s.jsx)("button",{onClick:()=>alert("button clicked!"),children:"Click me!"})]})]})}function p(t={}){const{wrapper:e}={...(0,n.R)(),...t.components};return e?(0,s.jsx)(e,{...t,children:(0,s.jsx)(i,{...t})}):i(t)}},8453:(t,e,o)=>{o.d(e,{R:()=>a,x:()=>c});var s=o(6540);const n={},r=s.createContext(n);function a(t){const e=s.useContext(r);return s.useMemo((function(){return"function"==typeof t?t(e):{...e,...t}}),[e,t])}function c(t){let e;return e=t.disableParentContext?"function"==typeof t.components?t.components(n):t.components||n:a(t.components),s.createElement(r.Provider,{value:e},t.children)}}}]);
|
||||