Compare commits

..

3 Commits

Author SHA1 Message Date
Jeff McCune
6894f45b6c (#165) Deploy Holos to Dev
This patch deploys holos to the dev environment on the k2 cluster.  It's
accessible at https://app.dev.k2.holos.run/ behind the auth proxy by
default.
2024-05-06 11:10:29 -07:00
Jeff McCune
89d25be837 (#161) Wrap main router outlet in <main></main> 2024-05-06 09:16:15 -07:00
Jeff McCune
5b33e48552 (#161) Reasonably advanced form modeling the reference platform
This form goes a good way toward capturing what we need to configure the
entire reference platform.  Elements and sections are responsive to
which cloud providers are selected, which achieves my goal of modeling a
reasonably advanced form using only JSON data produced by CUE.

To write the form via the API:

    cue export ./forms/platform/ --out json \
      | jq '{platform_id: "'${platformId}'", fields: .spec.fields}' \
      | grpcurl -H "x-oidc-id-token: $(holos token)" -d @ ${host}:443 \
      holos.platform.v1alpha1.PlatformService.PutForm
2024-05-04 20:16:09 -07:00
11 changed files with 1178 additions and 99 deletions

View File

@@ -18,6 +18,7 @@ import "encoding/yaml"
Issuer?: [Name=_]: #Issuer & {metadata: name: Name}
Gateway?: [Name=_]: #Gateway & {metadata: name: Name}
ConfigMap?: [Name=_]: #ConfigMap & {metadata: name: Name}
ServiceAccount?: [Name=_]: #ServiceAccount & {metadata: name: Name}
Deployment?: [_]: #Deployment
StatefulSet?: [_]: #StatefulSet

View File

@@ -0,0 +1,73 @@
package holos
let Namespace = "dev-holos"
let Holos = "holos"
// spec represents the output provided to holos
spec: components: KubernetesObjectsList: [
#KubernetesObjects & {
metadata: name: "dev-holos-app"
apiObjectMap: OBJECTS.apiObjectMap
},
]
// OBJECTS represents the kubernetes api objects to manage.
let OBJECTS = #APIObjects & {
apiObjects: Deployment: holos: {
metadata: {
name: Holos
namespace: Namespace
labels: app: Holos
}
spec: {
selector: matchLabels: app: Holos
template: metadata: labels: {
app: Holos
"sidecar.istio.io/inject": "true"
}
strategy: rollingUpdate: maxSurge: 1
strategy: rollingUpdate: maxUnavailable: 0
template: {
spec: {
serviceAccountName: Holos
securityContext: seccompProfile: type: "RuntimeDefault"
containers: [
{
name: Holos
image: "271053619184.dkr.ecr.us-east-2.amazonaws.com/holos-run/holos-server/holos:0.73.0"
imagePullPolicy: "Always"
env: [
{
name: "TZ"
value: "America/Los_Angeles"
},
{
name: "DATABASE_URL"
valueFrom: secretKeyRef: {
key: "uri"
name: "holos-pguser-holos"
}
},
]
ports: [
{
containerPort: 3000
name: "http"
protocol: "TCP"
},
]
securityContext: capabilities: drop: ["ALL"]
securityContext: allowPrivilegeEscalation: false
securityContext: runAsNonRoot: true
resources: limits: {
cpu: "0.25"
memory: "256Mi"
}
resources: requests: resources.limits
},
]
}
}
}
}
}

View File

@@ -0,0 +1,129 @@
package holos
let Namespace = "dev-holos"
let Holos = "holos"
// spec represents the output provided to holos
spec: components: KubernetesObjectsList: [
#KubernetesObjects & {
metadata: name: "dev-holos-infra"
apiObjectMap: OBJECTS.apiObjectMap
},
]
let Metadata = {
name: Holos
namespace: Namespace
labels: app: Holos
}
// OBJECTS represents the kubernetes api objects to manage.
let OBJECTS = #APIObjects & {
// Postgres
// Deployment
// VirtualService
apiObjects: ServiceAccount: holos: {
metadata: Metadata
imagePullSecrets: [{name: "kube-system-ecr-image-pull-creds"}]
}
apiObjects: PostgresCluster: holos: {
apiVersion: "postgres-operator.crunchydata.com/v1beta1"
metadata: Metadata
spec: {
image: "registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-16.1-0"
instances: [{
affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: [{
podAffinityTerm: {
labelSelector: matchLabels: "postgres-operator.crunchydata.com/cluster": "holos"
topologyKey: "kubernetes.io/hostname"
}
weight: 1
}]
dataVolumeClaimSpec: {
accessModes: ["ReadWriteOnce"]
resources: requests: storage: "1Gi"
}
name: "db"
replicas: 1
}]
port: 5432
postgresVersion: 16
users: [{
databases: ["holos"]
name: "holos"
options: "SUPERUSER"
}]
backups: pgbackrest: {
global: {
"archive-async": "y"
"archive-push-queue-max": "100MiB"
"spool-path": "/pgdata/backups"
}
image: "registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.47-2"
repos: [{
name: "repo1"
volume: volumeClaimSpec: {
accessModes: ["ReadWriteOnce"]
resources: requests: storage: "1Gi"
}
}]
}
}
}
apiObjects: Service: holos: {
apiVersion: "v1"
metadata: Metadata
spec: {
type: "ClusterIP"
selector: app: "holos"
ports: [{
appProtocol: "http2"
name: "http"
port: 3000
protocol: "TCP"
targetPort: 3000
}, {
appProtocol: "http"
name: "metrics"
port: 9090
protocol: "TCP"
targetPort: 9090
}]
}
}
apiObjects: VirtualService: holos: {
apiVersion: "networking.istio.io/v1beta1"
metadata: Metadata
spec: {
gateways: ["istio-ingress/default"]
hosts: [
"app.dev.holos.run",
"app.dev.\(#ClusterName).holos.run",
]
http: [{
match: [{
uri: prefix: "/ui"
}]
name: "ui"
route: [{
destination: {
host: "holos"
port: number: 3000
}
}]
}, {
name: "api"
route: [{
destination: {
host: "holos"
port: number: 3000
}
}]
}]
}
}
}

View File

@@ -3,5 +3,6 @@ USER root
WORKDIR /app
ADD bin bin
RUN chown -R app: /app
USER app
# Kubernetes requires the user to be numeric
USER 8192
ENTRYPOINT bin/holos server

View File

@@ -33,7 +33,8 @@
</span>
<app-profile-button [claims$]="claims$"></app-profile-button>
</mat-toolbar>
<!-- Add Content Here -->
<router-outlet></router-outlet>
<main class="main-content">
<router-outlet></router-outlet>
</main>
</mat-sidenav-content>
</mat-sidenav-container>

View File

@@ -3,8 +3,8 @@
<mat-tab label="Detail">
<div class="grid-container">
<form [formGroup]="form" (ngSubmit)="onSubmit(model)">
<formly-form [model]="model" [form]="form" [fields]="fields"></formly-form>
<button type="submit" mat-flat-button color="primary">Submit</button>
<formly-form [model]="model" [fields]="fields" [options]="options" [form]="form"></formly-form>
<button type="submit" mat-flat-button color="primary" [disabled]="!form.valid">Submit</button>
</form>
</div>
</mat-tab>
@@ -14,6 +14,11 @@
<pre>{{ model | json }}</pre>
</div>
</mat-tab>
<mat-tab label="Form State">
<div class="grid-container">
<pre>{{ options.formState | json }}</pre>
</div>
</mat-tab>
<mat-tab label="Fields">
<div class="grid-container">
<pre>{{ fields | json }}</pre>

View File

@@ -34,23 +34,35 @@ export class PlatformDetailComponent implements OnDestroy {
private destroy$: Subject<any> = new Subject<any>();
form = new FormGroup({});
model: JsonValue = {};
options: FormlyFormOptions = {};
fields: FormlyFieldConfig[] = [];
model: JsonValue = {};
// Use form state to store the model for nested forms
// Refer to https://formly.dev/docs/examples/form-options/form-state/
options: FormlyFormOptions = {
formState: {
model: this.model,
},
};
private setModel(model: JsonValue) {
if (model) {
this.model = model
this.options.formState.model = model
}
}
onSubmit(model: JsonValue) {
// if (this.form.valid) {
console.log(model)
this.platformService
.putModel(this.platformId, model)
.pipe(takeUntil(this.destroy$))
.subscribe(resp => {
const model = JSON.parse(JSON.stringify(resp.model))
if (model) {
this.model = model
}
})
// }
if (this.form.valid) {
console.log(model)
this.platformService
.putModel(this.platformId, model)
.pipe(takeUntil(this.destroy$))
.subscribe(resp => {
if (resp.model !== undefined) {
this.setModel(resp.model.toJson())
}
})
}
}
@Input()
@@ -60,14 +72,15 @@ export class PlatformDetailComponent implements OnDestroy {
.getForm(platformId)
.pipe(takeUntil(this.destroy$))
.subscribe(resp => {
if (resp.model !== undefined) {
this.setModel(resp.model.toJson())
}
if (resp.fields !== undefined) {
// NOTE: We could map fields to mix in javascript functions. Refer to
// NOTE: We could mix functions into the json data via mapped fields,
// but consider carefully before doing so. Refer to
// https://formly.dev/docs/examples/other/json-powered
this.fields = resp.fields.map(field => field.toJson() as FormlyFieldConfig)
}
if (resp.model !== undefined) {
this.model = resp.model.toJson()
}
})
}

View File

@@ -64,7 +64,222 @@ let Platform = formsv1.#Platform & {
}
}
sections: privacy: {
sections: cloud: {
displayName: "Cloud Providers"
description: "Select the services that provide resources for the platform."
fieldConfigs: {
providers: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Providers"
description: "Select the cloud providers the platform builds upon."
multiple: true
selectAllOption: "Select All"
options: [
{value: "aws", label: "Amazon Web Services"},
{value: "gcp", label: "Google Cloud Platform"},
{value: "azure", label: "Microsoft Azure"},
{value: "cloudflare", label: "Cloudflare"},
{value: "github", label: "GitHub"},
{value: "ois", label: "Open Infrastructure Services"},
{value: "onprem", label: "On Premises", disabled: true},
]
}
}
}
}
sections: aws: {
displayName: "Amazon Web Services"
description: "Provide the information necessary for Holos to manage AWS resources to provide the platform."
expressions: hide: "!\(AWSSelected)"
fieldConfigs: {
primaryRoleARN: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Holos Admin Role ARN"
description: "Enter the AWS Role ARN Holos will use to bootstrap resources. For example, arn:aws:iam::123456789012:role/HolosAdminAccess"
pattern: "^arn:.*"
minLength: 4
required: true
}
validation: messages: {
pattern: "Must be a valid ARN. Refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html"
}
}
regions: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Regions"
description: "Select the AWS regions this platform operates in."
multiple: true
required: true
selectAllOption: "Select All"
options: AWSRegions
}
}
}
}
sections: gcp: {
displayName: "Google Cloud Platform"
description: "Use this form to configure platform level GCP settings."
expressions: hide: "!\(GCPSelected)"
fieldConfigs: {
regions: {
// https://formly.dev/docs/api/ui/material/select/
type: "select"
props: {
label: "Select Regions"
description: "Select the GCP regions this platform operates in."
multiple: true
selectAllOption: "Select All"
// gcloud compute regions list --format=json | jq '.[] | {value: .name, label: .description}' regions.json | jq -s | cue export --out cue
options: GCPRegions
}
}
gcpProjectID: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Project ID"
description: "Enter the project id where the provisioner cluster resides."
pattern: "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$"
minLength: 6
maxLength: 30
required: true
}
validation: messages: {
pattern: "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
}
}
gcpProjectNumber: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Project Number"
// note type number here
type: "number"
description: "Enter the project number where the provisioner cluster resides."
pattern: "^[0-9]+$"
required: true
}
validation: messages: {
pattern: "Must be a valid project number."
}
}
provisionerCABundle: {
type: "input"
props: {
label: "Provisioner CA Bundle"
description: "Enter the provisioner cluster ca bundle. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.certificate-authority-data}'"
pattern: "^[0-9a-zA-Z]+=*$"
required: true
}
validation: messages: {
pattern: "Must be a base64 encoded pem encoded certificate bundle."
}
}
provisionerURL: {
type: "input"
props: {
label: "Provisioner URL"
description: "Enter the URL of the provisioner cluster API endpoint. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.server}'"
pattern: "^https://.*$"
required: true
}
validation: messages: {
pattern: "Must be a https:// URL."
}
}
}
}
sections: cloudflare: {
displayName: "Cloudflare"
description: "Cloudflare is primarily used for DNS automation."
expressions: hide: "!" + CloudflareSelected
fieldConfigs: {
email: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Account Email"
description: "Enter the Cloudflare email address to manage DNS"
minLength: 3
required: true
}
}
}
}
sections: github: {
displayName: "GitHub"
description: "GitHub is primarily used to host Git repositories and execute Actions workflows."
expressions: hide: "!\(GitHubSelected)"
fieldConfigs: {
primaryOrg: {
// https://formly.dev/docs/api/ui/material/input
type: "input"
props: {
label: "Organization"
description: "Enter the primary GitHub organization associed with the platform."
pattern: "^(?!-)(?!.*--)([a-zA-Z0-9]|-){1,39}$"
minLength: 1
maxLength: 39
required: true
}
validation: messages: {
pattern: "All characters must be either a hyphen or alphanumeric. Cannot start with a hyphen. Cannot include consecutive hyphens."
}
}
}
}
sections: backups: {
displayName: "Backups"
description: "Configure platform level data backup settings. Requires AWS."
fieldConfigs: {
s3bucket: {
// https://formly.dev/docs/api/ui/material/input
type: "select"
props: {
label: "S3 Bucket Region"
description: "Select the S3 Bucket Region."
multiple: true
options: AWSRegions
}
expressions: {
// Disable the control if AWS is not selected.
"props.disabled": "!" + AWSSelected
// Required if AWS is selected.
"props.required": AWSSelected
// Change the label depending on AWS
"props.description": AWSSelected + " ? '\(props.description)' : 'Enable AWS in the Cloud Provider section to configure backups.'"
}
}
}
}
_sections: privacy: {
displayName: "Data Privacy"
description: "Configure data privacy aspects of the platform."
@@ -106,8 +321,7 @@ let Platform = formsv1.#Platform & {
}
}
// https://v5.formly.dev/ui/material
sections: terms: {
_sections: terms: {
displayName: "Terms and Conditions"
description: "Example of a boolean checkbox."
@@ -133,3 +347,80 @@ let Platform = formsv1.#Platform & {
// Provide the output form fields
Platform.Form
let GCPRegions = [
{value: "africa-south1", label: "africa-south1"},
{value: "asia-east1", label: "asia-east1"},
{value: "asia-east2", label: "asia-east2"},
{value: "asia-northeast1", label: "asia-northeast1"},
{value: "asia-northeast2", label: "asia-northeast2"},
{value: "asia-northeast3", label: "asia-northeast3"},
{value: "asia-south1", label: "asia-south1"},
{value: "asia-south2", label: "asia-south2"},
{value: "asia-southeast1", label: "asia-southeast1"},
{value: "asia-southeast2", label: "asia-southeast2"},
{value: "australia-southeast1", label: "australia-southeast1"},
{value: "australia-southeast2", label: "australia-southeast2"},
{value: "europe-central2", label: "europe-central2"},
{value: "europe-north1", label: "europe-north1"},
{value: "europe-southwest1", label: "europe-southwest1"},
{value: "europe-west1", label: "europe-west1"},
{value: "europe-west10", label: "europe-west10"},
{value: "europe-west12", label: "europe-west12"},
{value: "europe-west2", label: "europe-west2"},
{value: "europe-west3", label: "europe-west3"},
{value: "europe-west4", label: "europe-west4"},
{value: "europe-west6", label: "europe-west6"},
{value: "europe-west8", label: "europe-west8"},
{value: "europe-west9", label: "europe-west9"},
{value: "me-central1", label: "me-central1"},
{value: "me-central2", label: "me-central2"},
{value: "me-west1", label: "me-west1"},
{value: "northamerica-northeast1", label: "northamerica-northeast1"},
{value: "northamerica-northeast2", label: "northamerica-northeast2"},
{value: "southamerica-east1", label: "southamerica-east1"},
{value: "southamerica-west1", label: "southamerica-west1"},
{value: "us-central1", label: "us-central1"},
{value: "us-east1", label: "us-east1"},
{value: "us-east4", label: "us-east4"},
{value: "us-east5", label: "us-east5"},
{value: "us-south1", label: "us-south1"},
{value: "us-west1", label: "us-west1"},
{value: "us-west2", label: "us-west2"},
{value: "us-west3", label: "us-west3"},
{value: "us-west4", label: "us-west4"},
]
let AWSRegions = [
{value: "us-east-1", label: "N. Virginia (us-east-1)"},
{value: "us-east-2", label: "Ohio (us-east-2)"},
{value: "us-west-1", label: "N. California (us-west-1)"},
{value: "us-west-2", label: "Oregon (us-west-2)"},
{value: "us-gov-west1", label: "US GovCloud West (us-gov-west1)"},
{value: "us-gov-east1", label: "US GovCloud East (us-gov-east1)"},
{value: "ca-central-1", label: "Canada (ca-central-1)"},
{value: "eu-north-1", label: "Stockholm (eu-north-1)"},
{value: "eu-west-1", label: "Ireland (eu-west-1)"},
{value: "eu-west-2", label: "London (eu-west-2)"},
{value: "eu-west-3", label: "Paris (eu-west-3)"},
{value: "eu-central-1", label: "Frankfurt (eu-central-1)"},
{value: "eu-south-1", label: "Milan (eu-south-1)"},
{value: "af-south-1", label: "Cape Town (af-south-1)"},
{value: "ap-northeast-1", label: "Tokyo (ap-northeast-1)"},
{value: "ap-northeast-2", label: "Seoul (ap-northeast-2)"},
{value: "ap-northeast-3", label: "Osaka (ap-northeast-3)"},
{value: "ap-southeast-1", label: "Singapore (ap-southeast-1)"},
{value: "ap-southeast-2", label: "Sydney (ap-southeast-2)"},
{value: "ap-east-1", label: "Hong Kong (ap-east-1)"},
{value: "ap-south-1", label: "Mumbai (ap-south-1)"},
{value: "me-south-1", label: "Bahrain (me-south-1)"},
{value: "sa-east-1", label: "São Paulo (sa-east-1)"},
{value: "cn-north-1", label: "Bejing (cn-north-1)"},
{value: "cn-northwest-1", label: "Ningxia (cn-northwest-1)"},
{value: "ap-southeast-3", label: "Jakarta (ap-southeast-3)"},
]
let AWSSelected = "formState.model.cloud?.providers?.includes(\"aws\")"
let GCPSelected = "formState.model.cloud?.providers?.includes(\"gcp\")"
let GitHubSelected = "formState.model.cloud?.providers?.includes(\"github\")"
let CloudflareSelected = "formState.model.cloud?.providers?.includes(\"cloudflare\")"

View File

@@ -26,16 +26,20 @@ package v1alpha1
fields: [...#FieldConfig]
}
// #ConfigSection represents a configuration section of the front end UI. For
// #ConfigSection represents a configuration section of the front end UI. For
// example, Organization config values. The fields of the section map to form
// input fields.
#ConfigSection: {
name: string // e.g. "org"
displayName: string // e.g. "Organization"
description: string
expressions: {[string]: string}
fieldConfigs: {[NAME=string]: #FieldConfig & {key: NAME}}
let Description = description
let Expressions = expressions
// Wrap the fields of the section into one FormlyFieldConfig
wrapper: #FieldConfig & {
@@ -44,6 +48,12 @@ package v1alpha1
wrappers: ["holos-panel"]
props: label: displayName
props: description: Description
for k, v in Expressions {
expressions: "\(k)": v
}
// Might need to initialize the default value for a fieldGroup
// https://github.com/ngx-formly/ngx-formly/issues/3667
fieldGroup: [for fc in fieldConfigs {fc}]
}
}
@@ -70,6 +80,7 @@ package v1alpha1
#FormlySelectProps
label: string
type?: string
placeholder?: string
description: string
required?: *true | false
@@ -84,6 +95,10 @@ package v1alpha1
messages?: [string]: string
}
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L66-L71
// We do not support validators because they must be javascript functions, not data.
validators?: "not supported"
// Refer to: https://github.com/ngx-formly/ngx-formly/blob/v6.3.0/src/core/src/lib/models/fieldconfig.ts#L115-L120
expressions?: [string]: string
hide?: true | false

View File

@@ -189,12 +189,14 @@ const BareForm = `
"props": {
"label": "Name",
"description": "DNS label, e.g. 'example'",
"pattern": "[a-z][0-9a-z]{4,29}",
"pattern": "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$",
"minLength": 3,
"maxLength": 30,
"required": true
},
"validation": {
"messages": {
"pattern": "It must be 6 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
"pattern": "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
}
},
"resetOnHide": true
@@ -205,7 +207,10 @@ const BareForm = `
"props": {
"label": "Domain",
"placeholder": "example.com",
"description": "DNS domain, e.g. 'example.com'"
"minLength": 3,
"maxLength": 100,
"description": "DNS domain, e.g. 'example.com'",
"required": true
},
"resetOnHide": true
},
@@ -215,7 +220,9 @@ const BareForm = `
"props": {
"label": "Display Name",
"placeholder": "Example Organization",
"description": "Display name, e.g. 'Example Organization'"
"description": "Display name, e.g. 'Example Organization'",
"maxLength": 100,
"required": true
},
"resetOnHide": true
},
@@ -225,90 +232,60 @@ const BareForm = `
"props": {
"label": "Contact Email",
"placeholder": "platform-team@example.com",
"description": "Technical contact email address"
"description": "Technical contact email address",
"required": true
},
"resetOnHide": true
}
]
},
{
"key": "privacy",
"key": "cloud",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Data Privacy",
"description": "Configure data privacy aspects of the platform."
"label": "Cloud Providers",
"description": "Select the services that provide resources for the platform."
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "country",
"key": "providers",
"type": "select",
"props": {
"label": "Select Planet",
"description": "Juridiction of applicable data privacy laws.",
"options": [
{
"value": "mercury",
"label": "Mercury"
},
{
"value": "venus",
"label": "Venus"
},
{
"value": "earth",
"label": "Earth"
},
{
"value": "mars",
"label": "Mars"
},
{
"value": "jupiter",
"label": "Jupiter"
},
{
"value": "saturn",
"label": "Saturn"
},
{
"value": "uranus",
"label": "Uranus"
},
{
"value": "neptune",
"label": "Neptune"
}
]
},
"resetOnHide": true
},
{
"key": "regions",
"type": "select",
"props": {
"label": "Select Regions",
"description": "Select the regions this platform operates in.",
"label": "Select Providers",
"description": "Select the cloud providers the platform builds upon.",
"multiple": true,
"selectAllOption": "Select All",
"options": [
{
"value": "us-east-2",
"label": "Ohio"
"value": "aws",
"label": "Amazon Web Services"
},
{
"value": "us-west-2",
"label": "Oregon"
"value": "gcp",
"label": "Google Cloud Platform"
},
{
"value": "eu-west-1",
"label": "Ireland"
"value": "azure",
"label": "Microsoft Azure"
},
{
"value": "eu-west-2",
"label": "London",
"value": "cloudflare",
"label": "Cloudflare"
},
{
"value": "github",
"label": "GitHub"
},
{
"value": "ois",
"label": "Open Infrastructure Services"
},
{
"value": "onprem",
"label": "On Premises",
"disabled": true
}
]
@@ -318,31 +295,604 @@ const BareForm = `
]
},
{
"key": "terms",
"key": "aws",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Terms and Conditions",
"description": "Example of a boolean checkbox."
"label": "Amazon Web Services",
"description": "Provide the information necessary for Holos to manage AWS resources to provide the platform."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"aws\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "didAgree",
"type": "checkbox",
"key": "primaryRoleARN",
"type": "input",
"props": {
"label": "Accept terms",
"description": "In order to proceed, please accept terms",
"pattern": "true",
"label": "Holos Admin Role ARN",
"description": "Enter the AWS Role ARN Holos will use to bootstrap resources. For example, arn:aws:iam::123456789012:role/HolosAdminAccess",
"pattern": "^arn:.*",
"minLength": 4,
"required": true
},
"validation": {
"messages": {
"pattern": "Please accept the terms"
"pattern": "Must be a valid ARN. Refer to https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html"
}
},
"resetOnHide": true
},
{
"key": "regions",
"type": "select",
"props": {
"label": "Select Regions",
"description": "Select the AWS regions this platform operates in.",
"multiple": true,
"required": true,
"selectAllOption": "Select All",
"options": [
{
"value": "us-east-1",
"label": "N. Virginia (us-east-1)"
},
{
"value": "us-east-2",
"label": "Ohio (us-east-2)"
},
{
"value": "us-west-1",
"label": "N. California (us-west-1)"
},
{
"value": "us-west-2",
"label": "Oregon (us-west-2)"
},
{
"value": "us-gov-west1",
"label": "US GovCloud West (us-gov-west1)"
},
{
"value": "us-gov-east1",
"label": "US GovCloud East (us-gov-east1)"
},
{
"value": "ca-central-1",
"label": "Canada (ca-central-1)"
},
{
"value": "eu-north-1",
"label": "Stockholm (eu-north-1)"
},
{
"value": "eu-west-1",
"label": "Ireland (eu-west-1)"
},
{
"value": "eu-west-2",
"label": "London (eu-west-2)"
},
{
"value": "eu-west-3",
"label": "Paris (eu-west-3)"
},
{
"value": "eu-central-1",
"label": "Frankfurt (eu-central-1)"
},
{
"value": "eu-south-1",
"label": "Milan (eu-south-1)"
},
{
"value": "af-south-1",
"label": "Cape Town (af-south-1)"
},
{
"value": "ap-northeast-1",
"label": "Tokyo (ap-northeast-1)"
},
{
"value": "ap-northeast-2",
"label": "Seoul (ap-northeast-2)"
},
{
"value": "ap-northeast-3",
"label": "Osaka (ap-northeast-3)"
},
{
"value": "ap-southeast-1",
"label": "Singapore (ap-southeast-1)"
},
{
"value": "ap-southeast-2",
"label": "Sydney (ap-southeast-2)"
},
{
"value": "ap-east-1",
"label": "Hong Kong (ap-east-1)"
},
{
"value": "ap-south-1",
"label": "Mumbai (ap-south-1)"
},
{
"value": "me-south-1",
"label": "Bahrain (me-south-1)"
},
{
"value": "sa-east-1",
"label": "São Paulo (sa-east-1)"
},
{
"value": "cn-north-1",
"label": "Bejing (cn-north-1)"
},
{
"value": "cn-northwest-1",
"label": "Ningxia (cn-northwest-1)"
},
{
"value": "ap-southeast-3",
"label": "Jakarta (ap-southeast-3)"
}
]
},
"resetOnHide": true
}
]
},
{
"key": "gcp",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Google Cloud Platform",
"description": "Use this form to configure platform level GCP settings."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"gcp\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "regions",
"type": "select",
"props": {
"label": "Select Regions",
"description": "Select the GCP regions this platform operates in.",
"multiple": true,
"selectAllOption": "Select All",
"options": [
{
"value": "africa-south1",
"label": "africa-south1"
},
{
"value": "asia-east1",
"label": "asia-east1"
},
{
"value": "asia-east2",
"label": "asia-east2"
},
{
"value": "asia-northeast1",
"label": "asia-northeast1"
},
{
"value": "asia-northeast2",
"label": "asia-northeast2"
},
{
"value": "asia-northeast3",
"label": "asia-northeast3"
},
{
"value": "asia-south1",
"label": "asia-south1"
},
{
"value": "asia-south2",
"label": "asia-south2"
},
{
"value": "asia-southeast1",
"label": "asia-southeast1"
},
{
"value": "asia-southeast2",
"label": "asia-southeast2"
},
{
"value": "australia-southeast1",
"label": "australia-southeast1"
},
{
"value": "australia-southeast2",
"label": "australia-southeast2"
},
{
"value": "europe-central2",
"label": "europe-central2"
},
{
"value": "europe-north1",
"label": "europe-north1"
},
{
"value": "europe-southwest1",
"label": "europe-southwest1"
},
{
"value": "europe-west1",
"label": "europe-west1"
},
{
"value": "europe-west10",
"label": "europe-west10"
},
{
"value": "europe-west12",
"label": "europe-west12"
},
{
"value": "europe-west2",
"label": "europe-west2"
},
{
"value": "europe-west3",
"label": "europe-west3"
},
{
"value": "europe-west4",
"label": "europe-west4"
},
{
"value": "europe-west6",
"label": "europe-west6"
},
{
"value": "europe-west8",
"label": "europe-west8"
},
{
"value": "europe-west9",
"label": "europe-west9"
},
{
"value": "me-central1",
"label": "me-central1"
},
{
"value": "me-central2",
"label": "me-central2"
},
{
"value": "me-west1",
"label": "me-west1"
},
{
"value": "northamerica-northeast1",
"label": "northamerica-northeast1"
},
{
"value": "northamerica-northeast2",
"label": "northamerica-northeast2"
},
{
"value": "southamerica-east1",
"label": "southamerica-east1"
},
{
"value": "southamerica-west1",
"label": "southamerica-west1"
},
{
"value": "us-central1",
"label": "us-central1"
},
{
"value": "us-east1",
"label": "us-east1"
},
{
"value": "us-east4",
"label": "us-east4"
},
{
"value": "us-east5",
"label": "us-east5"
},
{
"value": "us-south1",
"label": "us-south1"
},
{
"value": "us-west1",
"label": "us-west1"
},
{
"value": "us-west2",
"label": "us-west2"
},
{
"value": "us-west3",
"label": "us-west3"
},
{
"value": "us-west4",
"label": "us-west4"
}
]
},
"resetOnHide": true
},
{
"key": "gcpProjectID",
"type": "input",
"props": {
"label": "Project ID",
"description": "Enter the project id where the provisioner cluster resides.",
"pattern": "^[a-z]([0-9a-z]|-){1,28}[0-9a-z]$",
"minLength": 6,
"maxLength": 30,
"required": true
},
"validation": {
"messages": {
"pattern": "It must be 3 to 30 lowercase letters, digits, or hyphens. It must start with a letter. Trailing hyphens are prohibited."
}
},
"resetOnHide": true
},
{
"key": "gcpProjectNumber",
"type": "input",
"props": {
"label": "Project Number",
"type": "number",
"description": "Enter the project number where the provisioner cluster resides.",
"pattern": "^[0-9]+$",
"required": true
},
"validation": {
"messages": {
"pattern": "Must be a valid project number."
}
},
"resetOnHide": true
},
{
"key": "provisionerCABundle",
"type": "input",
"props": {
"label": "Provisioner CA Bundle",
"description": "Enter the provisioner cluster ca bundle. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.certificate-authority-data}'",
"pattern": "^[0-9a-zA-Z]+=*$",
"required": true
},
"validation": {
"messages": {
"pattern": "Must be a base64 encoded pem encoded certificate bundle."
}
},
"resetOnHide": true
},
{
"key": "provisionerURL",
"type": "input",
"props": {
"label": "Provisioner URL",
"description": "Enter the URL of the provisioner cluster API endpoint. kubectl config view --minify --flatten -ojsonpath='{.clusters[0].cluster.server}'",
"pattern": "^https://.*$",
"required": true
},
"validation": {
"messages": {
"pattern": "Must be a https:// URL."
}
},
"resetOnHide": true
}
]
},
{
"key": "cloudflare",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Cloudflare",
"description": "Cloudflare is primarily used for DNS automation."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"cloudflare\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "email",
"type": "input",
"props": {
"label": "Account Email",
"description": "Enter the Cloudflare email address to manage DNS",
"minLength": 3,
"required": true
},
"resetOnHide": true
}
]
},
{
"key": "github",
"wrappers": [
"holos-panel"
],
"props": {
"label": "GitHub",
"description": "GitHub is primarily used to host Git repositories and execute Actions workflows."
},
"expressions": {
"hide": "!formState.model.cloud?.providers?.includes(\"github\")"
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "primaryOrg",
"type": "input",
"props": {
"label": "Organization",
"description": "Enter the primary GitHub organization associed with the platform.",
"pattern": "^(?!-)(?!.*--)([a-zA-Z0-9]|-){1,39}$",
"minLength": 1,
"maxLength": 39,
"required": true
},
"validation": {
"messages": {
"pattern": "All characters must be either a hyphen or alphanumeric. Cannot start with a hyphen. Cannot include consecutive hyphens."
}
},
"resetOnHide": true
}
]
},
{
"key": "backups",
"wrappers": [
"holos-panel"
],
"props": {
"label": "Backups",
"description": "Configure platform level data backup settings. Requires AWS."
},
"resetOnHide": true,
"fieldGroup": [
{
"key": "s3bucket",
"type": "select",
"props": {
"label": "S3 Bucket Region",
"description": "Select the S3 Bucket Region.",
"multiple": true,
"options": [
{
"value": "us-east-1",
"label": "N. Virginia (us-east-1)"
},
{
"value": "us-east-2",
"label": "Ohio (us-east-2)"
},
{
"value": "us-west-1",
"label": "N. California (us-west-1)"
},
{
"value": "us-west-2",
"label": "Oregon (us-west-2)"
},
{
"value": "us-gov-west1",
"label": "US GovCloud West (us-gov-west1)"
},
{
"value": "us-gov-east1",
"label": "US GovCloud East (us-gov-east1)"
},
{
"value": "ca-central-1",
"label": "Canada (ca-central-1)"
},
{
"value": "eu-north-1",
"label": "Stockholm (eu-north-1)"
},
{
"value": "eu-west-1",
"label": "Ireland (eu-west-1)"
},
{
"value": "eu-west-2",
"label": "London (eu-west-2)"
},
{
"value": "eu-west-3",
"label": "Paris (eu-west-3)"
},
{
"value": "eu-central-1",
"label": "Frankfurt (eu-central-1)"
},
{
"value": "eu-south-1",
"label": "Milan (eu-south-1)"
},
{
"value": "af-south-1",
"label": "Cape Town (af-south-1)"
},
{
"value": "ap-northeast-1",
"label": "Tokyo (ap-northeast-1)"
},
{
"value": "ap-northeast-2",
"label": "Seoul (ap-northeast-2)"
},
{
"value": "ap-northeast-3",
"label": "Osaka (ap-northeast-3)"
},
{
"value": "ap-southeast-1",
"label": "Singapore (ap-southeast-1)"
},
{
"value": "ap-southeast-2",
"label": "Sydney (ap-southeast-2)"
},
{
"value": "ap-east-1",
"label": "Hong Kong (ap-east-1)"
},
{
"value": "ap-south-1",
"label": "Mumbai (ap-south-1)"
},
{
"value": "me-south-1",
"label": "Bahrain (me-south-1)"
},
{
"value": "sa-east-1",
"label": "São Paulo (sa-east-1)"
},
{
"value": "cn-north-1",
"label": "Bejing (cn-north-1)"
},
{
"value": "cn-northwest-1",
"label": "Ningxia (cn-northwest-1)"
},
{
"value": "ap-southeast-3",
"label": "Jakarta (ap-southeast-3)"
}
]
},
"expressions": {
"props.disabled": "!formState.model.cloud?.providers?.includes(\"aws\")",
"props.required": "formState.model.cloud?.providers?.includes(\"aws\")",
"props.description": "formState.model.cloud?.providers?.includes(\"aws\") ? 'Select the S3 Bucket Region.' : 'Enable AWS in the Cloud Provider section to configure backups.'"
},
"resetOnHide": true
}
]
}

View File

@@ -1 +1 @@
0
1