feat(docs): setup Gridsome for the website
8
docs/.gitignore
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
*.log
|
||||
.cache
|
||||
.DS_Store
|
||||
src/.temp
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
.env.*
|
||||
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
@@ -19,7 +19,7 @@ A lightweight Kubernetes within your laptop can be very handy for Kubernetes-nat
|
||||
|
||||
#### By `k3d`
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# Install K3d cli by brew in Mac, or your preferred way
|
||||
$ brew install k3d
|
||||
|
||||
@@ -46,7 +46,7 @@ CONTAINER ID IMAGE COMMAND CREATED
|
||||
|
||||
#### By `kind`
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# # Install kind cli by brew in Mac, or your preferred way
|
||||
$ brew install kind
|
||||
|
||||
@@ -99,13 +99,13 @@ The `fork-clone-contribute-pr` flow is common for contributing to OSS projects l
|
||||
Let's assume you've forked it into your GitHub namespace, say `myuser`, and then you can clone it with Git protocol.
|
||||
Do remember to change the `myuser` to yours.
|
||||
|
||||
```sh
|
||||
```shell
|
||||
$ git clone git@github.com:myuser/capsule.git && cd capsule
|
||||
```
|
||||
|
||||
It's a good practice to add the upsteam as the remote too so we can easily fetch and merge the upstream to our fork:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
$ git remote add upstream https://github.com/clastix/capsule.git
|
||||
$ git remote -vv
|
||||
origin git@github.com:myuser/capsule.git (fetch)
|
||||
@@ -116,7 +116,7 @@ upstream https://github.com/clastix/capsule.git (push)
|
||||
|
||||
## Build & deploy Capsule
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# Download the project dependencies
|
||||
$ go mod download
|
||||
|
||||
@@ -173,13 +173,13 @@ During development, we prefer that the code is running within our IDE locally, i
|
||||
|
||||
Such a setup can be illustrated as below diagram:
|
||||
|
||||

|
||||

|
||||
|
||||
To achieve that, there are some necessary steps we need to walk through, which have been made as a `make` target within our `Makefile`.
|
||||
|
||||
So the TL;DR answer is:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# If you haven't installed or run `make deploy` before, do it first
|
||||
# Note: please retry if you saw errors
|
||||
$ make deploy
|
||||
@@ -196,7 +196,7 @@ This is a very common setup for typical Kubernetes Operator development so we'd
|
||||
|
||||
We need to scale the existing replicas of `capsule-controller-manager` to 0 to avoid reconciliation competition between the Pod(s) and the code running outside of the cluster, in our preferred IDE for example.
|
||||
|
||||
```sh
|
||||
```shell
|
||||
$ kubectl -n capsule-system scale deployment capsule-controller-manager --replicas=0
|
||||
deployment.apps/capsule-controller-manager scaled
|
||||
```
|
||||
@@ -205,7 +205,7 @@ deployment.apps/capsule-controller-manager scaled
|
||||
|
||||
Running webhooks requires TLS, we can prepare the TLS key pair in our development env to handle HTTPS requests.
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# Prepare a simple OpenSSL config file
|
||||
# Do remember to export LAPTOP_HOST_IP before running this command
|
||||
$ cat > _tls.cnf <<EOF
|
||||
@@ -246,7 +246,7 @@ By default, the webhooks will be registered with the services, which will route
|
||||
|
||||
We need to _delegate_ the controllers' and webbooks' services to the code running in our IDE by patching the `MutatingWebhookConfiguration` and `ValidatingWebhookConfiguration`.
|
||||
|
||||
```sh
|
||||
```shell
|
||||
# Export your laptop's IP with the 9443 port exposed by controllers/webhooks' services
|
||||
$ export WEBHOOK_URL="https://${LAPTOP_HOST_IP}:9443"
|
||||
|
||||
@@ -284,14 +284,14 @@ $ kubectl get ValidatingWebhookConfiguration capsule-validating-webhook-configur
|
||||
|
||||
Now we can run Capsule controllers with webhooks outside of the Kubernetes cluster:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
$ export NAMESPACE=capsule-system && export TMPDIR=/tmp/
|
||||
$ go run .
|
||||
```
|
||||
|
||||
To verify that, we can open a new console and create a new Tenant:
|
||||
|
||||
```sh
|
||||
```shell
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: capsule.clastix.io/v1beta1
|
||||
kind: Tenant
|
||||
@@ -356,4 +356,4 @@ For example, if you're using [Visual Studio Code](https://code.visualstudio.com)
|
||||
}
|
||||
```
|
||||
|
||||
Please refer to [contributing.md](contributing.md) for more details while contributing.
|
||||
Please refer to [contributing](/docs/contributing) for more details while contributing.
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
Currently, the Capsule ecosystem comprises the following:
|
||||
|
||||
* [Capsule Operator](./operator/overview.md)
|
||||
* [Capsule Proxy](./proxy/overview.md)
|
||||
* [Capsule Lens extension](./lens-extension/overview.md)
|
||||
* [Capsule Operator](/docs/operator/overview)
|
||||
* [Capsule Proxy](/docs/proxy/overview)
|
||||
* [Capsule Lens extension](/docs/lens-extension/overview)
|
||||
@@ -6,7 +6,7 @@ First, thanks for your interest in Capsule, any contribution is welcome!
|
||||
|
||||
The first step is to set up your local development environment.
|
||||
|
||||
Please follow the [Capsule Development Guide](dev-guide.md) for details.
|
||||
Please follow the [Capsule Development Guide](/docs/dev-guide) for details.
|
||||
|
||||
## Code convention
|
||||
|
||||
@@ -22,7 +22,7 @@ You can easily check them issuing the _Make_ recipe `golint`.
|
||||
golangci-lint run -c .golangci.yml
|
||||
```
|
||||
|
||||
> Enabled linters and related options are defined in the [.golanci.yml file](../../.golangci.yml)
|
||||
> Enabled linters and related options are defined in the [.golanci.yml file](https://github.com/clastix/capsule/blob/master/.golangci.yml)
|
||||
|
||||
### goimports
|
||||
|
||||
@@ -68,7 +68,7 @@ Users authenticated through an _OIDC token_ must have in their token:
|
||||
]
|
||||
```
|
||||
|
||||
The [hack/create-user.sh](../../hack/create-user.sh) can help you set up a dummy `kubeconfig` for the `alice` user acting as owner of a tenant called `oil`
|
||||
The [hack/create-user.sh](https://github.com/clastix/capsule/blob/master/hack/create-user.sh) can help you set up a dummy `kubeconfig` for the `alice` user acting as owner of a tenant called `oil`
|
||||
|
||||
```bash
|
||||
./hack/create-user.sh alice oil
|
||||
@@ -107,4 +107,4 @@ Error from server (Forbidden): pods is forbidden: User "alice" cannot list resou
|
||||
```
|
||||
|
||||
# What’s next
|
||||
The Tenant Owners have full administrative permissions limited to only the namespaces in the assigned tenant. However, their permissions can be controlled by the Cluster Admin by setting rules and policies on the assigned tenant. See the [use cases](./use-cases/overview.md) page for more getting more cool things you can do with Capsule.
|
||||
The Tenant Owners have full administrative permissions limited to only the namespaces in the assigned tenant. However, their permissions can be controlled by the Cluster Admin by setting rules and policies on the assigned tenant. See the [use cases](/docs/operator/use-cases/overview) page for more getting more cool things you can do with Capsule.
|
||||
@@ -9,7 +9,7 @@ Capsule Operator can be easily installed on a Managed Kubernetes Service. Since
|
||||
- MutatingAdmissionWebhook
|
||||
- ValidatingAdmissionWebhook
|
||||
|
||||
* [AWS EKS](./aws-eks.md)
|
||||
* [AWS EKS](/docs/managed-kubernetes/aws-eks)
|
||||
* CoAKS - Capsule over Azure Kubernetes Service
|
||||
* Google Cloud GKE
|
||||
* IBM Cloud
|
||||
30
docs/content/operator/mtb/sig-multitenancy-bench.md
Normal file
@@ -0,0 +1,30 @@
|
||||
# Meet the multi-tenancy benchmark MTB
|
||||
Actually, there's no yet a real standard for the multi-tenancy model in Kubernetes, although the [SIG multi-tenancy group](https://github.com/kubernetes-sigs/multi-tenancy) is working on that. SIG multi-tenancy drafted a generic validation schema appliable to generic multi-tenancy projects. Multi-Tenancy Benchmarks [MTB](https://github.com/kubernetes-sigs/multi-tenancy/tree/master/benchmarks) are guidelines for multi-tenant configuration of Kubernetes clusters. Capsule is an open source multi-tenancy operator and we decided to meet the requirements of MTB.
|
||||
|
||||
> N.B. At the time of writing, the MTB is in development and not ready for usage. Strictly speaking, we do not claim official conformance to MTB, but just to adhere to the multi-tenancy requirements and best practices promoted by MTB.
|
||||
|
||||
|MTB Benchmark |MTB Profile|Capsule Version|Conformance|Notes |
|
||||
|--------------|-----------|---------------|-----------|-------|
|
||||
|[Block access to cluster resources](/docs/operator/mtb/block-access-to-cluster-resources)|L1|v0.1.0|✓|---|
|
||||
|[Block access to multitenant resources](/docs/operator/mtb/block-access-to-multitenant-resources)|L1|v0.1.0|✓|---|
|
||||
|[Block access to other tenant resources](/docs/operator/mtb/block-access-to-other-tenant-resources)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Block add capabilities](/docs/operator/mtb/block-add-capabilities)|L1|v0.1.0|✓|---|
|
||||
|[Require always imagePullPolicy](/docs/operator/mtb/require-always-imagepullpolicy)|L1|v0.1.0|✓|---|
|
||||
|[Require run as non-root user](/docs/operator/mtb/require-run-as-non-root-user)|L1|v0.1.0|✓|---|
|
||||
|[Block privileged containers](/docs/operator/mtb/block-privileged-containers)|L1|v0.1.0|✓|---|
|
||||
|[Block privilege escalation](/docs/operator/mtb/block-privilege-escalation)|L1|v0.1.0|✓|---|
|
||||
|[Configure namespace resource quotas](/docs/operator/mtb/configure-namespace-resource-quotas)|L1|v0.1.0|✓|---|
|
||||
|[Block modification of resource quotas](/docs/operator/mtb/block-modification-of-resource-quotas)|L1|v0.1.0|✓|---|
|
||||
|[Configure namespace object limits](/docs/operator/mtb/configure-namespace-object-limits)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host path volumes](/docs/operator/mtb/block-use-of-host-path-volumes)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host networking and ports](/docs/operator/mtb/block-use-of-host-networking-and-ports)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host PID](/docs/operator/mtb/block-use-of-host-pid)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host IPC](/docs/operator/mtb/block-use-of-host-ipc)|L1|v0.1.0|✓|---|
|
||||
|[Block use of NodePort services](/docs/operator/mtb/block-use-of-nodeport-services)|L1|v0.1.0|✓|---|
|
||||
|[Require PersistentVolumeClaim for storage](/docs/operator/mtb/require-persistentvolumeclaim-for-storage)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Require PV reclaim policy of delete](/docs/operator/mtb/require-reclaim-policy-of-delete)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Block use of existing PVs](/docs/operator/mtb/block-use-of-existing-persistent-volumes)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Block network access across tenant namespaces](/docs/operator/mtb/block-network-access-across-tenant-namespaces)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Allow self-service management of Network Policies](/docs/operator/mtb/allow-self-service-management-of-network-policies)|L2|v0.1.0|✓|---|
|
||||
|[Allow self-service management of Roles](/docs/operator/mtb/allow-self-service-management-of-roles)|L2|v0.1.0|✓|MTB draft|
|
||||
|[Allow self-service management of Role Bindings](/docs/operator/mtb/allow-self-service-management-of-rolebindings)|L2|v0.1.0|✓|MTB draft|
|
||||
10
docs/content/operator/overview.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# Kubernetes Operator
|
||||
|
||||
* [Getting Started](/docs/operator/getting-started)
|
||||
* [Use Cases](/docs/operator/use-cases/overview)
|
||||
* [SIG Multi-tenancy benchmark](/docs/operator/mtb/sig-multitenancy-bench)
|
||||
* [Run on Managed Kubernetes Services](/docs/operator/managed-kubernetes/overview)
|
||||
* [Monitoring Capsule](/docs/operator/monitoring)
|
||||
* [References](/docs/operator/references)
|
||||
* [Contributing](/docs/operator/contributing)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Reference
|
||||
|
||||
* [Custom Resource Definition](#customer-resource-definition)
|
||||
* [Capsule Configuration](#capsule-configuration)
|
||||
* [Capsule Permissions](#capsule-permissions)
|
||||
* [Admission Controllers](#admission-controller)
|
||||
* [Command Options](#command-options)
|
||||
* [Created Resources](#created-resources)
|
||||
* [Custom Resource Definition](/docs/operator/references/#customer-resource-definition)
|
||||
* [Capsule Configuration](/docs/operator/references/#capsule-configuration)
|
||||
* [Capsule Permissions](/docs/operator/references/#capsule-permissions)
|
||||
* [Admission Controllers](/docs/operator/references/#admission-controller)
|
||||
* [Command Options](/docs/operator/references/#command-options)
|
||||
* [Created Resources](/docs/operator/references/#created-resources)
|
||||
|
||||
## Custom Resource Definition
|
||||
|
||||
@@ -49,4 +49,4 @@ silver Active 2 3d13h
|
||||
|
||||
# What’s next
|
||||
|
||||
See how Bill, the cluster admin, can prevent creating services with specific service types. [Disabling Service Types](./service-type.md).
|
||||
See how Bill, the cluster admin, can prevent creating services with specific service types. [Disabling Service Types](/docs/operator/use-cases/service-type).
|
||||
@@ -107,4 +107,4 @@ Error from server (Cannot exceed Namespace quota: please, reach out to the syste
|
||||
The enforcement on the maximum number of namespaces per Tenant is the responsibility of the Capsule controller via its Dynamic Admission Webhook capability.
|
||||
|
||||
# What’s next
|
||||
See how Alice, the tenant owner, can assign different user roles in the tenant. [Assign permissions](./permissions.md).
|
||||
See how Alice, the tenant owner, can assign different user roles in the tenant. [Assign permissions](/docs/operator/use-cases/permissions).
|
||||
@@ -75,4 +75,4 @@ With the above example, Capsule is leaving the tenant owner to create namespaced
|
||||
> Take Note: a tenant owner having the admin scope on its namespaces only, does not have the permission to create Custom Resources Definitions (CRDs) because this requires a cluster admin permission level. Only Bill, the cluster admin, can create CRDs. This is a known limitation of any multi-tenancy environment based on a single Kubernetes cluster.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can set taints on Alice's namespaces. [Taint namespaces](./taint-namespaces.md).
|
||||
See how Bill, the cluster admin, can set taints on Alice's namespaces. [Taint namespaces](/docs/operator/use-cases/taint-namespaces).
|
||||
@@ -26,4 +26,4 @@ EOF
|
||||
Doing this, Alice will not be able to use `oil.bigorg.com`, being the tenant-owner of `gas`.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin can protect specific labels and annotations on Nodes from modifications by Tenant Owners. [Denying specific user-defined labels or annotations on Nodes](./node-labels-and-annotations.md).
|
||||
See how Bill, the cluster admin can protect specific labels and annotations on Nodes from modifications by Tenant Owners. [Denying specific user-defined labels or annotations on Nodes](/docs/operator/use-cases/node-labels-and-annotations).
|
||||
@@ -77,4 +77,4 @@ EOF
|
||||
When a collision is detected at scope defined by `spec.ingressOptions.hostnameCollisionScope`, the creation of the Ingress resource will be rejected by the Validation Webhook enforcing it. When `hostnameCollisionScope=Disabled`, no collision detection is made at all.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can assign a Storage Class to Alice's tenant. [Assign Storage Classes](./storage-classes.md).
|
||||
See how Bill, the cluster admin, can assign a Storage Class to Alice's tenant. [Assign Storage Classes](/docs/operator/use-cases/storage-classes).
|
||||
@@ -29,4 +29,4 @@ Any attempt of Alice to use a disallowed `imagePullPolicies` value is denied by
|
||||
|
||||
# What’s next
|
||||
|
||||
See how Bill, the cluster admin, can assign trusted images registries to Alice's tenant. [Assign Trusted Images Registries](./images-registries.md).
|
||||
See how Bill, the cluster admin, can assign trusted images registries to Alice's tenant. [Assign Trusted Images Registries](/docs/operator/use-cases/images-registries).
|
||||
@@ -31,4 +31,4 @@ A Pod running `internal.registry.foo.tld/capsule:latest` as registry will be all
|
||||
Any attempt of Alice to use a not allowed `containerRegistries` value is denied by the Validation Webhook enforcing it.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can assign Pod Security Policies to Alice's tenant. [Assign Pod Security Policies](./pod-security-policies.md).
|
||||
See how Bill, the cluster admin, can assign Pod Security Policies to Alice's tenant. [Assign Pod Security Policies](/docs/operator/use-cases/pod-security-policies).
|
||||
@@ -49,4 +49,4 @@ EOF
|
||||
Any attempt of Alice to use a non-valid Ingress Class, or missing it, is denied by the Validation Webhook enforcing it.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can assign a set of dedicated ingress hostnames to Alice's tenant. [Assign Ingress Hostnames](./ingress-hostnames.md).
|
||||
See how Bill, the cluster admin, can assign a set of dedicated ingress hostnames to Alice's tenant. [Assign Ingress Hostnames](/docs/operator/use-cases/ingress-hostnames).
|
||||
@@ -50,4 +50,4 @@ EOF
|
||||
Any attempt of Alice to use a non-valid hostname is denied by the Validation Webhook enforcing it.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can control the hostname collision in Ingresses. [Control hostname collision in ingresses](./hostname-collision.md).
|
||||
See how Bill, the cluster admin, can control the hostname collision in Ingresses. [Control hostname collision in ingresses](/docs/operator/use-cases//hostname-collision).
|
||||
@@ -91,4 +91,4 @@ EOF
|
||||
|
||||
# What’s next
|
||||
|
||||
See how Bill, the cluster admin, can cordon all the Namespaces belonging to a Tenant. [Cordoning a Tenant](./cordoning-tenant.md).
|
||||
See how Bill, the cluster admin, can cordon all the Namespaces belonging to a Tenant. [Cordoning a Tenant](/docs/operator/use-cases/cordoning-tenant).
|
||||
@@ -25,4 +25,4 @@ EOF
|
||||
```
|
||||
|
||||
# What’s next
|
||||
Let's check it out how to restore Tenants after a Velero Backup. [Velero Backup Restoration](./velero-backup-restoration.md).
|
||||
Let's check it out how to restore Tenants after a Velero Backup. [Velero Backup Restoration](/docs/operator/use-cases/velero-backup-restoration).
|
||||
@@ -99,4 +99,4 @@ kubectl -n oil-production delete networkpolicy production-network-policy
|
||||
Any attempt of Alice to delete the tenant network policy defined in the tenant manifest is denied by the Validation Webhook enforcing it.
|
||||
|
||||
# What’s next
|
||||
See how Bill can enforce the Pod containers image pull policy to `Always` to avoid leaking of private images when running on shared nodes. [Enforcing Pod containers image PullPolicy](./images-pullpolicy.md)
|
||||
See how Bill can enforce the Pod containers image pull policy to `Always` to avoid leaking of private images when running on shared nodes. [Enforcing Pod containers image PullPolicy](/docs/operator/use-cases/images-pullpolicy)
|
||||
@@ -1,6 +1,6 @@
|
||||
# Denying specific user-defined labels or annotations on Nodes
|
||||
|
||||
When using `capsule` together with [capsule-proxy](https://github.com/clastix/capsule-proxy), Bill can allow Tenant Owners to [modify Nodes](../../proxy/overview.md).
|
||||
When using `capsule` together with [capsule-proxy](https://github.com/clastix/capsule-proxy), Bill can allow Tenant Owners to [modify Nodes](/docs/proxy/overview).
|
||||
|
||||
By default, it will allow tenant owners to add and modify any label or annotation on their nodes.
|
||||
|
||||
@@ -61,4 +61,4 @@ no
|
||||
```
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can assign an Ingress Class to Alice's tenant. [Assign Ingress Classes](./ingress-classes.md).
|
||||
See how Bill, the cluster admin, can assign an Ingress Class to Alice's tenant. [Assign Ingress Classes](/docs/operator/use-cases/ingress-classes).
|
||||
51
docs/content/operator/use-cases/overview.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Use cases for Capsule
|
||||
Using Capsule, a cluster admin can implement complex multi-tenant scenarios for both public and private deployments. Here is a list of common scenarios addressed by Capsule.
|
||||
|
||||
# Container as a Service (CaaS)
|
||||
***Acme Corp***, our sample organization, built a Container as a Service platform (CaaS), based on Kubernetes to serve multiple lines of business. Each line of business has its team of engineers that are responsible for the development, deployment, and operating of their digital products.
|
||||
|
||||
To simplify the usage of Capsule in this scenario, we'll work with the following actors:
|
||||
|
||||
* ***Bill***:
|
||||
he is the cluster administrator from the operations department of Acme Corp. and he is in charge of administration and maintains the CaaS platform.
|
||||
|
||||
* ***Alice***:
|
||||
she works as the IT Project Leader in the Oil & Gas Business Units. These are two new lines of business at Acme Corp. Alice is responsible for all the strategic IT projects in the two LOBs. She also is responsible for a team made of different job responsibilities (developers, administrators, SRE engineers, etc.) working in separate departments.
|
||||
|
||||
* ***Joe***:
|
||||
he works at Acme Corp, as a lead developer of a distributed team in Alice's organization. Joe is responsible for developing a mission-critical project in the Oil market.
|
||||
|
||||
* ***Bob***:
|
||||
he is the head of Engineering for the Water Business Unit, the main and historical line of business at Acme Corp. He is responsible for the development, deployment, and operation of multiple digital products in production for a large set of customers.
|
||||
|
||||
Use Capsule to address any of the following scenarios:
|
||||
|
||||
* [Assign Tenant Ownership](/docs/operator/use-cases/tenant-ownership)
|
||||
* [Create Namespaces](/docs/operator/use-cases/create-namespaces)
|
||||
* [Assign Permissions](/docs/operator/use-cases/permissions)
|
||||
* [Enforce Resources Quotas and Limits](/docs/operator/use-cases/resources-quota-limits)
|
||||
* [Enforce Pod Priority Classes](/docs/operator/use-cases/pod-priority-classes)
|
||||
* [Assign specific Node Pools](/docs/operator/use-cases/nodes-pool)
|
||||
* [Assign Ingress Classes](/docs/operator/use-cases/ingress-classes)
|
||||
* [Assign Ingress Hostnames](/docs/operator/use-cases/ingress-hostnames)
|
||||
* [Control hostname collision in Ingresses](/docs/operator/use-cases/hostname-collision)
|
||||
* [Assign Storage Classes](/docs/operator/use-cases/storage-classes)
|
||||
* [Assign Network Policies](/docs/operator/use-cases/network-policies)
|
||||
* [Enforce Containers image PullPolicy](/docs/operator/use-cases/images-pullpolicy)
|
||||
* [Assign Trusted Images Registries](/docs/operator/use-cases/images-registries)
|
||||
* [Assign Pod Security Policies](/docs/operator/use-cases/pod-security-policies)
|
||||
* [Create Custom Resources](/docs/operator/use-cases/custom-resources)
|
||||
* [Taint Namespaces](/docs/operator/use-cases/taint-namespaces)
|
||||
* [Assign multiple Tenants](/docs/operator/use-cases/multiple-tenants)
|
||||
* [Cordon Tenants](/docs/operator/use-cases/cordoning-tenant)
|
||||
* [Disable Service Types](/docs/operator/use-cases/service-type)
|
||||
* [Taint Services](/docs/operator/use-cases/taint-services)
|
||||
* [Allow adding labels and annotations on namespaces](/docs/operator/use-cases/namespace-labels-and-annotations)
|
||||
* [Velero Backup Restoration](/docs/operator/use-cases/velero-backup-restoration)
|
||||
* [Deny Wildcard Hostnames](/docs/operator/use-cases/deny-wildcard-hostnames)
|
||||
* [Denying specific user-defined labels or annotations on Nodes](/docs/operator/use-cases/deny-specific-user-defined-labels-or-annotations-on-nodes)
|
||||
|
||||
> NB: as we improve Capsule, more use cases about multi-tenancy and cluster governance will be covered.
|
||||
|
||||
# What’s next
|
||||
Now let's see how the cluster admin onboards a new tenant. [Onboarding a new tenant](/docs/operator/use-cases/onboarding).
|
||||
@@ -51,4 +51,4 @@ no
|
||||
> Please, note the user `joe`, in the example above, is not acting as tenant owner. He can just operate in `oil-development` namespace as admin.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, sets resources quota and limits for Alice's tenant. [Enforce resources quota and limits](./resources-quota-limits.md).
|
||||
See how Bill, the cluster admin, sets resources quota and limits for Alice's tenant. [Enforce resources quota and limits](/docs/operator/use-cases/resources-quota-limits).
|
||||
@@ -32,4 +32,4 @@ If a Pod is going to use a non-allowed _Priority Class_, it will be rejected by
|
||||
|
||||
# What’s next
|
||||
|
||||
See how Bill, the cluster admin, can assign a pool of nodes to Alice's tenant. [Assign a nodes pool](./nodes-pool.md).
|
||||
See how Bill, the cluster admin, can assign a pool of nodes to Alice's tenant. [Assign a nodes pool](/docs/operator/use-cases/nodes-pool).
|
||||
@@ -80,4 +80,4 @@ roleRef:
|
||||
With the above example, Capsule is forbidding any authenticated user in `oil-production` namespace to run privileged pods and to perform privilege escalation as declared by the Cluster Role `psp:privileged`.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can assign to Alice the permissions to create custom resources in her tenant. [Create Custom Resources](./custom-resources.md).
|
||||
See how Bill, the cluster admin, can assign to Alice the permissions to create custom resources in her tenant. [Create Custom Resources](/docs/operator/use-cases/custom-resources).
|
||||
@@ -249,4 +249,4 @@ no
|
||||
|
||||
# What’s next
|
||||
|
||||
See how Bill, the cluster admin, can enforce the PriorityClass of Pods running of Alice's tenant namespaces. [Enforce Pod Priority Classes](./pod-priority-classes.md)
|
||||
See how Bill, the cluster admin, can enforce the PriorityClass of Pods running of Alice's tenant namespaces. [Enforce Pod Priority Classes](/docs/operator/use-cases/pod-priority-classes)
|
||||
@@ -68,4 +68,4 @@ EOF
|
||||
With the above configuration, any attempt of Alice to create a Service of type `LoadBalancer` is denied by the Validation Webhook enforcing it. Default value is `true`.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can set taints on the Alice's services. [Taint services](./taint-services.md).
|
||||
See how Bill, the cluster admin, can set taints on the Alice's services. [Taint services](/docs/operator/use-cases/taint-services).
|
||||
@@ -41,4 +41,4 @@ EOF
|
||||
Any attempt of Alice to use a non-valid Storage Class, or missing it, is denied by the Validation Webhook enforcing it.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can assign Network Policies to Alice's tenant. [Assign Network Policies](./network-policies.md).
|
||||
See how Bill, the cluster admin, can assign Network Policies to Alice's tenant. [Assign Network Policies](/docs/operator/use-cases/network-policies).
|
||||
@@ -25,4 +25,4 @@ EOF
|
||||
When Alice creates a namespace, this will inherit the given label and/or annotation.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can assign multiple tenants to Alice. [Assign multiple tenants to an owner](./multiple-tenants.md).
|
||||
See how Bill, the cluster admin, can assign multiple tenants to Alice. [Assign multiple tenants to an owner](/docs/operator/use-cases/multiple-tenants).
|
||||
@@ -25,4 +25,4 @@ EOF
|
||||
When Alice creates a service in a namespace, this will inherit the given label and/or annotation.
|
||||
|
||||
# What’s next
|
||||
See how Bill, the cluster admin, can protect specific labels and annotations on Namespaces from modifications by Alice. [Denying specific user-defined labels or annotations on Namespaces](./namespace-labels-and-annotations.md).
|
||||
See how Bill, the cluster admin, can protect specific labels and annotations on Namespaces from modifications by Alice. [Denying specific user-defined labels or annotations on Namespaces](/docs/operator/use-cases/namespace-labels-and-annotations).
|
||||
@@ -158,4 +158,4 @@ system:authenticated
|
||||
```
|
||||
|
||||
# What’s next
|
||||
See how a tenant owner, creates new namespaces. [Create namespaces](./create-namespaces.md).
|
||||
See how a tenant owner, creates new namespaces. [Create namespaces](/docs/operator/use-cases/create-namespaces).
|
||||
@@ -24,4 +24,4 @@ In this way, only the tenants **gas** and **oil** will be restored.
|
||||
|
||||
# What's next
|
||||
|
||||
See how Bill, the cluster admin, can deny wildcard hostnames to a Tenant. [Deny Wildcard Hostnames](./deny-wildcard-hostnames.md)
|
||||
See how Bill, the cluster admin, can deny wildcard hostnames to a Tenant. [Deny Wildcard Hostnames](/docs/operator/use-cases/deny-wildcard-hostnames)
|
||||
@@ -48,7 +48,7 @@ The `capsule-proxy` can be deployed in standalone mode, e.g. running as a pod br
|
||||
Optionally, it can be deployed as a sidecar container in the backend of a dashboard.
|
||||
Running outside a Kubernetes cluster is also viable, although a valid `KUBECONFIG` file must be provided, using the environment variable `KUBECONFIG` or the default file in `$HOME/.kube/config`.
|
||||
|
||||
An Helm Chart is available [here](./charts/capsule-proxy/README.md).
|
||||
An Helm Chart is available [here](https://github.com/clastix/capsule/blob/master/charts/capsule/README.md).
|
||||
|
||||
## Does it work with kubectl?
|
||||
|
||||
@@ -331,7 +331,7 @@ oil-production Active 2m
|
||||
# What’s next
|
||||
Have a fun with `capsule-proxy`:
|
||||
|
||||
* [Standalone Installation](./standalone.md)
|
||||
* [Sidecar Installation](./sidecar.md)
|
||||
* [OIDC Authentication](./oidc-auth.md)
|
||||
* [Contributing](./contributing.md)
|
||||
* [Standalone Installation](/docs/proxy/standalone)
|
||||
* [Sidecar Installation](/docs/proxy/sidecar)
|
||||
* [OIDC Authentication](/docs/proxy/oidc-auth)
|
||||
* [Contributing](/docs/proxy/contributing)
|
||||
@@ -112,5 +112,5 @@ data:
|
||||
|
||||
After starting the dashboard, login as a Tenant Owner user, e.g. `alice` according to the used authentication method, and check you can see only owned namespaces.
|
||||
|
||||
The `capsule-proxy` can be deployed in standalone mode, in order to be used with a command line tools like `kubectl`. See [Standalone Installation](./standalone.md).
|
||||
The `capsule-proxy` can be deployed in standalone mode, in order to be used with a command line tools like `kubectl`. See [Standalone Installation](/docs/proxy/standalone).
|
||||
|
||||
@@ -12,7 +12,7 @@ You can use an Ingress Controller to expose the `capsule-proxy` endpoint in SSL
|
||||
```
|
||||
|
||||
## Configure Capsule
|
||||
Make sure to have a working instance of the Capsule Operator in your Kubernetes cluster before to attempt to use `capsule-proxy`. Please, refer to the Capsule Operator [documentation](../operator/overview.md) for instructions.
|
||||
Make sure to have a working instance of the Capsule Operator in your Kubernetes cluster before to attempt to use `capsule-proxy`. Please, refer to the Capsule Operator [documentation](/docs/operator/overview) for instructions.
|
||||
|
||||
You should also have one or more tenants defined, e.g. `oil` and `gas` and they are assigned to the user `alice`.
|
||||
|
||||
@@ -63,4 +63,4 @@ Currently, the service account used for `capsule-proxy` needs to have `cluster-a
|
||||
## Configuring client-only dashboards
|
||||
If you're using a client-only dashboard, for example [Lens](https://k8slens.dev/), the `capsule-proxy` can be used as in the previous `kubectl` example since Lens just needs for a `kubeconfig` file. Assuming to use a `kubeconfig` file containing a valid OIDC token released for the `alice` user, you can access the cluster with Lens dashboard and see only namespaces belonging to the Alice's tenants.
|
||||
|
||||
For web based dashboards, like the [Kubernetes Dashboard](https://github.com/kubernetes/dashboard), the `capsule-proxy` can be installed as sidecar container. See [Sidecar Installation](./sidecar.md).
|
||||
For web based dashboards, like the [Kubernetes Dashboard](https://github.com/kubernetes/dashboard), the `capsule-proxy` can be installed as sidecar container. See [Sidecar Installation](/docs/proxy/sidecar).
|
||||
49
docs/gridsome.config.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// This is where project configuration and plugin options are located.
|
||||
// Learn more: https://gridsome.org/docs/config
|
||||
|
||||
// Changes here require a server restart.
|
||||
// To restart press CTRL + C in terminal and run `gridsome develop`
|
||||
|
||||
module.exports = {
|
||||
siteName: 'Capsule Documentation',
|
||||
titleTemplate: 'Capsule Documentation | %s',
|
||||
siteDescription: 'Documentation of Capsule, multi-tenant Operator for Kubernetes',
|
||||
icon: {
|
||||
favicon: './src/assets/favicon.png',
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
use: "gridsome-plugin-tailwindcss",
|
||||
|
||||
options: {
|
||||
tailwindConfig: './tailwind.config.js',
|
||||
// presetEnvConfig: {},
|
||||
// shouldImport: false,
|
||||
// shouldTimeTravel: false
|
||||
}
|
||||
},
|
||||
{
|
||||
use: '@gridsome/source-filesystem',
|
||||
options: {
|
||||
baseDir: './content',
|
||||
path: '**/*.md',
|
||||
pathPrefix: '/docs',
|
||||
typeName: 'MarkdownPage',
|
||||
remark: {
|
||||
externalLinksTarget: '_blank',
|
||||
externalLinksRel: ['noopener', 'noreferrer'],
|
||||
plugins: [
|
||||
'@gridsome/remark-prismjs'
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
chainWebpack: config => {
|
||||
const svgRule = config.module.rule('svg')
|
||||
svgRule.uses.clear()
|
||||
svgRule
|
||||
.use('vue-svg-loader')
|
||||
.loader('vue-svg-loader')
|
||||
}
|
||||
}
|
||||
316
docs/gridsome.server.js
Normal file
@@ -0,0 +1,316 @@
|
||||
// Server API makes it possible to hook into various parts of Gridsome
|
||||
// on server-side and add custom data to the GraphQL data layer.
|
||||
// Learn more: https://gridsome.org/docs/server-api/
|
||||
|
||||
// Changes here require a server restart.
|
||||
// To restart press CTRL + C in terminal and run `gridsome develop`
|
||||
|
||||
module.exports = function (api) {
|
||||
api.loadSource(actions => {
|
||||
// Use the Data Store API here: https://gridsome.org/docs/data-store-api/
|
||||
const sidebar = actions.addCollection({
|
||||
typeName: 'Sidebar'
|
||||
})
|
||||
|
||||
sidebar.addNode({
|
||||
sections: [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: '/docs/'
|
||||
},
|
||||
{
|
||||
label: 'Dev guide',
|
||||
path: '/docs/dev-guide'
|
||||
},
|
||||
{
|
||||
label: 'Contributing',
|
||||
path: '/docs/contributing'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Capsule Operator',
|
||||
items: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
path: '/docs/operator/getting-started'
|
||||
},
|
||||
{
|
||||
label: 'Monitoring Capsule',
|
||||
path: '/docs/operator/monitoring'
|
||||
},
|
||||
{
|
||||
label: 'References',
|
||||
path: '/docs/operator/references'
|
||||
},
|
||||
{
|
||||
label: 'Contributing',
|
||||
path: '/docs/operator/contributing'
|
||||
},
|
||||
{
|
||||
title: 'Run on Managed Kubernetes',
|
||||
subItems: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: '/docs/operator/managed-kubernetes/overview'
|
||||
},
|
||||
{
|
||||
label: 'AWS EKS',
|
||||
path: '/docs/operator/managed-kubernetes/aws-eks'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'SIG Multi-tenancy benchmark',
|
||||
subItems: [
|
||||
{
|
||||
label: 'SIG Multi-tenancy benchmark',
|
||||
path: '/docs/operator/mtb/sig-multitenancy-bench'
|
||||
},
|
||||
{
|
||||
label: 'Block access to cluster resources',
|
||||
path: '/docs/operator/mtb/block-access-to-cluster-resources'
|
||||
},
|
||||
{
|
||||
label: 'Block access to multitenant resources',
|
||||
path: '/docs/operator/mtb/block-access-to-multitenant-resources'
|
||||
},
|
||||
{
|
||||
label: 'Block access to other tenant resources',
|
||||
path: '/docs/operator/mtb/block-access-to-other-tenant-resources'
|
||||
},
|
||||
{
|
||||
label: 'Block add capabilities',
|
||||
path: '/docs/operator/mtb/block-add-capabilities'
|
||||
},
|
||||
{
|
||||
label: 'Require always imagePullPolicy',
|
||||
path: '/docs/operator/mtb/require-always-imagepullpolicy'
|
||||
},
|
||||
{
|
||||
label: 'Require run as non-root user',
|
||||
path: '/docs/operator/mtb/require-run-as-non-root-user'
|
||||
},
|
||||
{
|
||||
label: 'Block privileged containers',
|
||||
path: '/docs/operator/mtb/block-privileged-containers'
|
||||
},
|
||||
{
|
||||
label: 'Block privilege escalation',
|
||||
path: '/docs/operator/mtb/block-privilege-escalation'
|
||||
},
|
||||
{
|
||||
label: 'Configure namespace resource quotas',
|
||||
path: '/docs/operator/mtb/configure-namespace-resource-quotas'
|
||||
},
|
||||
{
|
||||
label: 'Block modification of resource quotas',
|
||||
path: '/docs/operator/mtb/block-modification-of-resource-quotas'
|
||||
},
|
||||
{
|
||||
label: 'Configure namespace object limits',
|
||||
path: '/docs/operator/mtb/configure-namespace-object-limits'
|
||||
},
|
||||
{
|
||||
label: 'Block use of host path volumes',
|
||||
path: '/docs/operator/mtb/block-use-of-host-path-volumes'
|
||||
},
|
||||
{
|
||||
label: 'Block use of host networking and ports',
|
||||
path: '/docs/operator/mtb/block-use-of-host-networking-and-ports'
|
||||
},
|
||||
{
|
||||
label: 'Block use of host PID',
|
||||
path: '/docs/operator/mtb/block-use-of-host-pid'
|
||||
},
|
||||
{
|
||||
label: 'Block use of host IPC',
|
||||
path: '/docs/operator/mtb/block-use-of-host-ipc'
|
||||
},
|
||||
{
|
||||
label: 'Block use of NodePort services',
|
||||
path: '/docs/operator/mtb/block-use-of-nodeport-services'
|
||||
},
|
||||
{
|
||||
label: 'Require PersistentVolumeClaim for storage',
|
||||
path: '/docs/operator/mtb/require-persistentvolumeclaim-for-storage'
|
||||
},
|
||||
{
|
||||
label: 'Require PV reclaim policy of delete',
|
||||
path: '/docs/operator/mtb/require-reclaim-policy-of-delete'
|
||||
},
|
||||
{
|
||||
label: 'Block use of existing PVs',
|
||||
path: '/docs/operator/mtb/block-use-of-existing-persistent-volumes'
|
||||
},
|
||||
{
|
||||
label: 'Block network access across tenant namespaces',
|
||||
path: '/docs/operator/mtb/block-network-access-across-tenant-namespaces'
|
||||
},
|
||||
{
|
||||
label: 'Allow self-service management of Network Policies',
|
||||
path: '/docs/operator/mtb/allow-self-service-management-of-network-policies'
|
||||
},
|
||||
{
|
||||
label: 'Allow self-service management of Roles',
|
||||
path: '/docs/operator/mtb/allow-self-service-management-of-roles'
|
||||
},
|
||||
{
|
||||
label: 'Allow self-service management of Role Bindings',
|
||||
path: '/docs/operator/mtb/allow-self-service-management-of-rolebindings'
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Use cases',
|
||||
subItems: [
|
||||
{
|
||||
label: 'Onboard Tenants',
|
||||
path: '/docs/operator/use-cases/overview'
|
||||
},
|
||||
{
|
||||
label: 'Assign Tenant Ownership',
|
||||
path: '/docs/operator/use-cases/tenant-ownership'
|
||||
},
|
||||
{
|
||||
label: 'Create Namespaces',
|
||||
path: '/docs/operator/use-cases/create-namespaces'
|
||||
},
|
||||
{
|
||||
label: 'Assign Permissions',
|
||||
path: '/docs/operator/use-cases/permissions'
|
||||
},
|
||||
{
|
||||
label: 'Enforce Resources Quotas and Limits',
|
||||
path: '/docs/operator/use-cases/resources-quota-limits'
|
||||
},
|
||||
{
|
||||
label: 'Enforce Pod Priority Classes',
|
||||
path: '/docs/operator/use-cases/pod-priority-classes'
|
||||
},
|
||||
{
|
||||
label: 'Assign specific Node Pools',
|
||||
path: '/docs/operator/use-cases/nodes-pool'
|
||||
},
|
||||
{
|
||||
label: 'Assign Ingress Classes',
|
||||
path: '/docs/operator/use-cases/ingress-classes'
|
||||
},
|
||||
{
|
||||
label: 'Assign Ingress Hostnames',
|
||||
path: '/docs/operator/use-cases/ingress-hostnames'
|
||||
},
|
||||
{
|
||||
label: 'Control hostname collision in Ingresses',
|
||||
path: '/docs/operator/use-cases/hostname-collision'
|
||||
},
|
||||
{
|
||||
label: 'Assign Storage Classes',
|
||||
path: '/docs/operator/use-cases/storage-classes'
|
||||
},
|
||||
{
|
||||
label: 'Assign Network Policies',
|
||||
path: '/docs/operator/use-cases/network-policies'
|
||||
},
|
||||
{
|
||||
label: 'Enforce Containers image PullPolicy',
|
||||
path: '/docs/operator/use-cases/images-pullpolicy'
|
||||
},
|
||||
{
|
||||
label: 'Assign Trusted Images Registries',
|
||||
path: '/docs/operator/use-cases/images-registries'
|
||||
},
|
||||
{
|
||||
label: 'Assign Pod Security Policies',
|
||||
path: '/docs/operator/use-cases/pod-security-policies'
|
||||
},
|
||||
{
|
||||
label: 'Create Custom Resources',
|
||||
path: '/docs/operator/use-cases/custom-resources'
|
||||
},
|
||||
{
|
||||
label: 'Taint Namespaces',
|
||||
path: '/docs/operator/use-cases/taint-namespaces'
|
||||
},
|
||||
{
|
||||
label: 'Assign multiple Tenants',
|
||||
path: '/docs/operator/use-cases/multiple-tenants'
|
||||
},
|
||||
{
|
||||
label: 'Cordon Tenants',
|
||||
path: '/docs/operator/use-cases/cordoning-tenant'
|
||||
},
|
||||
{
|
||||
label: 'Disable Service Types',
|
||||
path: '/docs/operator/use-cases/service-type'
|
||||
},
|
||||
{
|
||||
label: 'Taint Services',
|
||||
path: '/docs/operator/use-cases/taint-services'
|
||||
},
|
||||
{
|
||||
label: 'Allow adding labels and annotations on namespaces',
|
||||
path: '/docs/operator/use-cases/namespace-labels-and-annotations'
|
||||
},
|
||||
{
|
||||
label: 'Velero Backup Restoration',
|
||||
path: '/docs/operator/use-cases/velero-backup-restoration'
|
||||
},
|
||||
{
|
||||
label: 'Deny Wildcard Hostnames',
|
||||
path: '/docs/operator/use-cases/deny-wildcard-hostnames'
|
||||
},
|
||||
{
|
||||
label: 'Denying specific user-defined labels or annotations on Nodes',
|
||||
path: '/docs/operator/use-cases/deny-specific-user-defined-labels-or-annotations-on-nodes'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Capsule Proxy',
|
||||
items: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: '/docs/proxy/overview'
|
||||
},
|
||||
{
|
||||
label: 'Standalone Installation',
|
||||
path: '/docs/proxy/standalone'
|
||||
},
|
||||
{
|
||||
label: 'Sidecar Installation',
|
||||
path: '/docs/proxy/sidecar'
|
||||
},
|
||||
{
|
||||
label: 'OIDC Authentication',
|
||||
path: '/docs/proxy/oidc-auth'
|
||||
},
|
||||
{
|
||||
label: 'Contributing',
|
||||
path: '/docs/proxy/contributing'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Capsule Lens extension',
|
||||
items: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: '/docs/lens-extension/overview'
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
api.createPages(({ createPage }) => {
|
||||
// Use the Pages API here: https://gridsome.org/docs/pages-api/
|
||||
})
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
# Meet the multi-tenancy benchmark MTB
|
||||
Actually, there's no yet a real standard for the multi-tenancy model in Kubernetes, although the [SIG multi-tenancy group](https://github.com/kubernetes-sigs/multi-tenancy) is working on that. SIG multi-tenancy drafted a generic validation schema appliable to generic multi-tenancy projects. Multi-Tenancy Benchmarks [MTB](https://github.com/kubernetes-sigs/multi-tenancy/tree/master/benchmarks) are guidelines for multi-tenant configuration of Kubernetes clusters. Capsule is an open source multi-tenancy operator and we decided to meet the requirements of MTB.
|
||||
|
||||
> N.B. At the time of writing, the MTB is in development and not ready for usage. Strictly speaking, we do not claim official conformance to MTB, but just to adhere to the multi-tenancy requirements and best practices promoted by MTB.
|
||||
|
||||
|MTB Benchmark |MTB Profile|Capsule Version|Conformance|Notes |
|
||||
|--------------|-----------|---------------|-----------|-------|
|
||||
|[Block access to cluster resources](block-access-to-cluster-resources.md)|L1|v0.1.0|✓|---|
|
||||
|[Block access to multitenant resources](block-access-to-multitenant-resources.md)|L1|v0.1.0|✓|---|
|
||||
|[Block access to other tenant resources](block-access-to-other-tenant-resources.md)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Block add capabilities](block-add-capabilities.md)|L1|v0.1.0|✓|---|
|
||||
|[Require always imagePullPolicy](require-always-imagepullpolicy.md)|L1|v0.1.0|✓|---|
|
||||
|[Require run as non-root user](require-run-as-non-root-user.md)|L1|v0.1.0|✓|---|
|
||||
|[Block privileged containers](block-privileged-containers.md)|L1|v0.1.0|✓|---|
|
||||
|[Block privilege escalation](block-privilege-escalation.md)|L1|v0.1.0|✓|---|
|
||||
|[Configure namespace resource quotas](configure-namespace-resource-quotas.md)|L1|v0.1.0|✓|---|
|
||||
|[Block modification of resource quotas](block-modification-of-resource-quotas.md)|L1|v0.1.0|✓|---|
|
||||
|[Configure namespace object limits](configure-namespace-object-limits.md)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host path volumes](block-use-of-host-path-volumes.md)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host networking and ports](block-use-of-host-networking-and-ports.md)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host PID](block-use-of-host-pid.md)|L1|v0.1.0|✓|---|
|
||||
|[Block use of host IPC](block-use-of-host-ipc.md)|L1|v0.1.0|✓|---|
|
||||
|[Block use of NodePort services](block-use-of-nodeport-services.md)|L1|v0.1.0|✓|---|
|
||||
|[Require PersistentVolumeClaim for storage](require-persistentvolumeclaim-for-storage.md)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Require PV reclaim policy of delete](require-reclaim-policy-of-delete.md)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Block use of existing PVs](block-use-of-existing-persistent-volumes.md)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Block network access across tenant namespaces](block-network-access-across-tenant-namespaces.md)|L1|v0.1.0|✓|MTB draft|
|
||||
|[Allow self-service management of Network Policies](allow-self-service-management-of-network-policies.md)|L2|v0.1.0|✓|---|
|
||||
|[Allow self-service management of Roles](allow-self-service-management-of-roles.md)|L2|v0.1.0|✓|MTB draft|
|
||||
|[Allow self-service management of Role Bindings](allow-self-service-management-of-rolebindings.md)|L2|v0.1.0|✓|MTB draft|
|
||||
@@ -1,10 +0,0 @@
|
||||
# Kubernetes Operator
|
||||
|
||||
* [Getting Started](./getting-started.md)
|
||||
* [Use Cases](./use-cases/overview.md)
|
||||
* [SIG Multi-tenancy benchmark](./mtb/sig-multitenancy-bench.md)
|
||||
* [Run on Managed Kubernetes Services](./managed-kubernetes/overview.md)
|
||||
* [Monitoring Capsule](./monitoring.md)
|
||||
* [References](./references.md)
|
||||
* [Contributing](./contributing.md)
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# Use cases for Capsule
|
||||
Using Capsule, a cluster admin can implement complex multi-tenant scenarios for both public and private deployments. Here is a list of common scenarios addressed by Capsule.
|
||||
|
||||
# Container as a Service (CaaS)
|
||||
***Acme Corp***, our sample organization, built a Container as a Service platform (CaaS), based on Kubernetes to serve multiple lines of business. Each line of business has its team of engineers that are responsible for the development, deployment, and operating of their digital products.
|
||||
|
||||
To simplify the usage of Capsule in this scenario, we'll work with the following actors:
|
||||
|
||||
* ***Bill***:
|
||||
he is the cluster administrator from the operations department of Acme Corp. and he is in charge of administration and maintains the CaaS platform.
|
||||
|
||||
* ***Alice***:
|
||||
she works as the IT Project Leader in the Oil & Gas Business Units. These are two new lines of business at Acme Corp. Alice is responsible for all the strategic IT projects in the two LOBs. She also is responsible for a team made of different job responsibilities (developers, administrators, SRE engineers, etc.) working in separate departments.
|
||||
|
||||
* ***Joe***:
|
||||
he works at Acme Corp, as a lead developer of a distributed team in Alice's organization. Joe is responsible for developing a mission-critical project in the Oil market.
|
||||
|
||||
* ***Bob***:
|
||||
he is the head of Engineering for the Water Business Unit, the main and historical line of business at Acme Corp. He is responsible for the development, deployment, and operation of multiple digital products in production for a large set of customers.
|
||||
|
||||
Use Capsule to address any of the following scenarios:
|
||||
|
||||
* [Assign Tenant Ownership](./tenant-ownership.md)
|
||||
* [Create Namespaces](./create-namespaces.md)
|
||||
* [Assign Permissions](./permissions.md)
|
||||
* [Enforce Resources Quotas and Limits](./resources-quota-limits.md)
|
||||
* [Enforce Pod Priority Classes](./pod-priority-classes.md)
|
||||
* [Assign specific Node Pools](./nodes-pool.md)
|
||||
* [Assign Ingress Classes](./ingress-classes.md)
|
||||
* [Assign Ingress Hostnames](./ingress-hostnames.md)
|
||||
* [Control hostname collision in Ingresses](./hostname-collision.md)
|
||||
* [Assign Storage Classes](./storage-classes.md)
|
||||
* [Assign Network Policies](./network-policies.md)
|
||||
* [Enforce Containers image PullPolicy](./images-pullpolicy.md)
|
||||
* [Assign Trusted Images Registries](./images-registries.md)
|
||||
* [Assign Pod Security Policies](./pod-security-policies.md)
|
||||
* [Create Custom Resources](./custom-resources.md)
|
||||
* [Taint Namespaces](./taint-namespaces.md)
|
||||
* [Assign multiple Tenants](./multiple-tenants.md)
|
||||
* [Cordon Tenants](./cordoning-tenant.md)
|
||||
* [Disable Service Types](./service-type.md)
|
||||
* [Taint Services](./taint-services.md)
|
||||
* [Allow adding labels and annotations on namespaces](./namespace-labels-and-annotations.md)
|
||||
* [Velero Backup Restoration](./velero-backup-restoration.md)
|
||||
* [Deny Wildcard Hostnames](./deny-wildcard-hostnames.md)
|
||||
|
||||
> NB: as we improve Capsule, more use cases about multi-tenancy and cluster governance will be covered.
|
||||
|
||||
# What’s next
|
||||
Now let's see how the cluster admin onboards a new tenant. [Onboarding a new tenant](./onboarding.md).
|
||||
12657
docs/package-lock.json
generated
Normal file
28
docs/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "doc-capsule",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"develop": "gridsome develop",
|
||||
"explore": "gridsome explore",
|
||||
"build": "gridsome build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@gridsome/remark-prismjs": "^0.5.0",
|
||||
"@gridsome/source-filesystem": "^0.6.2",
|
||||
"@gridsome/transformer-remark": "^0.6.4",
|
||||
"fuse.js": "^6.4.6",
|
||||
"gridsome": "^0.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.8.8",
|
||||
"gridsome-plugin-tailwindcss": "^4.1.1",
|
||||
"postcss": "^7.0.39",
|
||||
"postcss-import": "^14.0.2",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"prism-themes": "^1.9.0",
|
||||
"sass": "^1.42.1",
|
||||
"sass-loader": "^10.1.1",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
|
||||
"vue-svg-loader": "^0.16.0"
|
||||
}
|
||||
}
|
||||
BIN
docs/src/assets/favicon.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
1
docs/src/assets/icon/arrow.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 10"><path d="M15 1.2l-7 7-7-7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
||||
|
After Width: | Height: | Size: 192 B |
1
docs/src/assets/icon/github.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-github"><path d="M9 19c-5 1.5-5-2.5-7-3m14 6v-3.87a3.37 3.37 0 0 0-.94-2.61c3.14-.35 6.44-1.54 6.44-7A5.44 5.44 0 0 0 20 4.77 5.07 5.07 0 0 0 19.91 1S18.73.65 16 2.48a13.38 13.38 0 0 0-7 0C6.27.65 5.09 1 5.09 1A5.07 5.07 0 0 0 5 4.77a5.44 5.44 0 0 0-1.5 3.78c0 5.42 3.3 6.61 6.44 7A3.37 3.37 0 0 0 9 18.13V22"></path></svg>
|
||||
|
After Width: | Height: | Size: 504 B |
1
docs/src/assets/icon/linkedin.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-linkedin"><path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z"></path><rect x="2" y="9" width="4" height="12"></rect><circle cx="4" cy="4" r="2"></circle></svg>
|
||||
|
After Width: | Height: | Size: 377 B |
1
docs/src/assets/icon/search.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
||||
|
After Width: | Height: | Size: 308 B |
1
docs/src/assets/icon/slack.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-slack"><path d="M14.5 10c-.83 0-1.5-.67-1.5-1.5v-5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5z"></path><path d="M20.5 10H19V8.5c0-.83.67-1.5 1.5-1.5s1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"></path><path d="M9.5 14c.83 0 1.5.67 1.5 1.5v5c0 .83-.67 1.5-1.5 1.5S8 21.33 8 20.5v-5c0-.83.67-1.5 1.5-1.5z"></path><path d="M3.5 14H5v1.5c0 .83-.67 1.5-1.5 1.5S2 16.33 2 15.5 2.67 14 3.5 14z"></path><path d="M14 14.5c0-.83.67-1.5 1.5-1.5h5c.83 0 1.5.67 1.5 1.5s-.67 1.5-1.5 1.5h-5c-.83 0-1.5-.67-1.5-1.5z"></path><path d="M15.5 19H14v1.5c0 .83.67 1.5 1.5 1.5s1.5-.67 1.5-1.5-.67-1.5-1.5-1.5z"></path><path d="M10 9.5C10 8.67 9.33 8 8.5 8h-5C2.67 8 2 8.67 2 9.5S2.67 11 3.5 11h5c.83 0 1.5-.67 1.5-1.5z"></path><path d="M8.5 5H10V3.5C10 2.67 9.33 2 8.5 2S7 2.67 7 3.5 7.67 5 8.5 5z"></path></svg>
|
||||
|
After Width: | Height: | Size: 976 B |
1
docs/src/assets/icon/twitter.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-twitter"><path d="M23 3a10.9 10.9 0 0 1-3.14 1.53 4.48 4.48 0 0 0-7.86 3v1A10.66 10.66 0 0 1 3 4s-4 9 5 13a11.64 11.64 0 0 1-7 2c9 5 20 0 20-11.5a4.5 4.5 0 0 0-.08-.83A7.72 7.72 0 0 0 23 3z"></path></svg>
|
||||
|
After Width: | Height: | Size: 385 B |
1
docs/src/assets/logo-full.svg
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
1
docs/src/assets/logo.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg viewBox="0 0 305 269" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M112 56.2c0-4.5-.4-9 0-13.5 0-1.2 2.1-3.1 3.4-3.3 7-.8 14-1.4 21-1.8 2.5 0 3.6-1 4.4-3 2-5.2 4-10.3 6.3-15.4.9-1.9.6-2.8-1.2-4-4.4-3.2-5-8.5-1.6-12.1 3.7-4 8.9-4.1 12.6-.2 3.5 3.7 3.1 9-1.3 12.1-2 1.4-2.1 2.5-1.3 4.5 2.2 5 4 10.3 6.4 15.2a6.2 6.2 0 0 0 4.2 3c7.6.7 15.2 1 22.8 1.6 2.7.1 4.2 1.2 4.2 4.1.1 4 .5 7.9.7 11.9-1.5 0-3 .2-4.5 0-12.6-.7-37.5-2.6-37.5-2.6L112 56.2Z" fill="#274872"/><path d="M13.4 193.8c2.9-4.2 5.7-8.3 8.7-12.4.8-1.1 2.1-2 3.2-2.9 8.7 2.8 17.2 6 26 8.2 7.3 1.8 7.6 1.3 11.1-5.6 9.6-18.2 19-36.5 28.5-54.9 3.5-6.9 7-13.8 10.3-20.8 1.9-4 1-5.3-3.3-5.1-5 .2-10 .6-15.2 1 1.8-2.4 3.3-4.9 5.2-7 4.5-4.9 9.2-9.6 13.8-14.4h5.5l43.9-3.2-.3 2.5v145.2c-15.7.9-30.4 1-47.4-.6-16-1-30.7-3.5-46.6-5.5A119 119 0 0 1 17.6 207c-4.8-2.4-5.9-5.4-4.3-10.4.3-.9.1-1.8.1-2.7Z" fill="#5783AB"/><path d="M150.9 224.4V76.7c5.6.1 11.4 0 17 .3 11.4.8 22.7 1.9 34 2.8 2.7 3 5.5 6.1 8 9.3 3.5 4.2 6.7 8.5 10 12.7-6-.5-12-1.2-18.1-1.5-4.2-.2-5 1.2-3.1 4.8l29.6 57.8c4 7.8 8.2 15.5 12.3 23.3 1.4 2.8 3.6 3.9 6.6 3.2l34.5-8.6c1.8 3 3.5 6.2 5.4 9.1 1 1.4 2.2 2.6 3.2 3.9 0 .5-.1 1.2 0 1.6 3.4 6.3 0 10-5.7 12.6a140.8 140.8 0 0 1-44.6 11.4l-38.6 3.7c-6.3.6-12.5 1.5-18.8 1.8-4.5.3-9.2-.1-13.8-.3H151l-.1-.2Z" fill="#EAECEC"/><path d="m281.7 180.8-34.5 8.6c-3 .7-5.2-.4-6.6-3.2-4-7.8-8.3-15.5-12.3-23.3L198.7 105c-1.9-3.6-1-5 3-4.8 6.1.3 12.1 1 18.2 1.5 1.9.4 2.5.1 5 3.7 17.1 24.4 37 47.8 54.9 71.7.8 1 1.3 2.4 2 3.6Z" fill="#5783AB"/><path d="m23.5 180 33.8 8.2c3 .7 5.1-.3 6.6-3.2L76 161.7c10-19.2 17.3-37.2 26.4-56.7 1.8-3.5.7-5-3.4-4.7-6 .3-10.1.4-16.2 1a10 10 0 0 0-3.8 3.4c-18 23.7-35.8 47.6-53.7 71.5-.8 1.1-1.3 2.5-1.9 3.7Z" fill="#EAECEC"/><path d="M290.4 193.8c0 .5-.2 1.2 0 1.6 3.3 6.3-.2 10-5.8 12.6a140.8 140.8 0 0 1-44.5 11.4l-38.7 3.7c-6.3.7-13 1.3-19 1.3-4.6 0-9 .4-13.6.2H151l-.1-.2c-15.4.4-28.8.4-46.2-1.2-16-1.4-32-3-47.9-5A119 119 0 0 1 17.6 207c-4.8-2.4-5.9-5.4-4.3-10.5.3-.8.1-1.7.1-2.6C7 196.3 2.1 200.2.6 207.5a25 25 0 0 0 5.7 21.7c1.5 1.8 2.4 3.2.1 5.2-.7.6-.8 3.3-.2 4a51 51 0 0 0 22.4 12.3c2.4.6 4.1 0 4.6-2.9.1-.6 1.7-1.6 2.3-1.5l26 6.2c1.9.4 2.6 1 1.4 3-1.4 2.4-.4 3.9 2 5 8.7 3.5 17.9 3.9 27 4.3 2.2 0 4.5-1 3.5-4-1-3.1.5-3.1 2.8-3 8.6.8 17.2 1.5 25.8 2 3.1.2 4 1 2.4 4s-.3 4.8 2.9 4.8c14.8 0 29.6.2 44.4-.1 4.6 0 5.1-1.3 3.4-5.6-.8-2.1-1-2.9 1.8-3a646 646 0 0 0 27.4-2.2c2.5-.2 3 .4 2.6 2.6-.6 2.3 0 4 2.7 4.1 9.5.2 18.9-.5 27.8-4.3 2-.9 3-2.1 1.7-4.3-1.7-2.7 0-3.1 2.2-3.6a901 901 0 0 0 24.6-6c1.8-.4 3-.7 3.2 1.6.3 3 2.1 3.4 4.6 2.6 8-2.3 15.5-6 21.6-12 .6-.6 1-3 .5-3.3-3.2-2.7-1.4-4.7.5-7 8-9.6 9.5-27.9-8-34.3Z" fill="#274872"/><path d="M192.8 70.5a4896.2 4896.2 0 0 1-39.1-1h-2.5l-.6.1V52.1c12.6 1 24.6 2 37.2 2.7h4.8l.2 15.7Z" fill="#EAECEC"/><path d="M111.8 71c6.7-.3 10-.4 16.8-.5l19-.5h2.4s.6-.6.6-.4V52.1c-12.6 1-21.3 2-33.8 2.7H112l-.2 16.2Z" fill="#5783AB"/><path d="m196 70.7-4-.3-40-1h-.1l-40 1-4 .3c-6.2.5-6.8 1.1-6.7 7.2 0 .7.2 1.3.3 2h5.7c15-1 28.7-2.1 43.6-3.2l46 3.1c1.8.2 3.7.1 5.6.1.1-.7.3-1.3.3-2 .2-6-.4-6.7-6.7-7.2Z" fill="#274872"/></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
49
docs/src/components/AppAccordion.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div>
|
||||
<button @click="toggleIsOpened()" class="flex items-center space-x-3 text-left">
|
||||
<slot name="title" />
|
||||
<icon-arrow
|
||||
class="w-2 transition-all duration-200 transform"
|
||||
:class="{
|
||||
'rotate-180': isOpened,
|
||||
'rotate-0': !isOpened,
|
||||
}"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
class="transition-all duration-400 overflow-hidden"
|
||||
:class="{
|
||||
'max-h-99': isOpened,
|
||||
'max-h-0': !isOpened,
|
||||
}"
|
||||
>
|
||||
<slot name="content" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconArrow from "~/assets/icon/arrow.svg?inline";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
IconArrow,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOpened: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleIsOpened() {
|
||||
this.isOpened = !this.isOpened;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.max-h-99 {
|
||||
max-height: 99rem;
|
||||
}
|
||||
</style>
|
||||
32
docs/src/components/AppButton.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<component
|
||||
:is="link ? 'g-link' : 'button'"
|
||||
:to="link"
|
||||
class="
|
||||
inline-block
|
||||
focus:outline-none
|
||||
font-medium
|
||||
rounded-md
|
||||
relative
|
||||
bg-primary
|
||||
py-3 px-8
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
link: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => undefined,
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
71
docs/src/components/AppFooter.vue
Normal file
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<footer class="py-8 bg-gray-900 bg-opacity-50">
|
||||
<div
|
||||
class="
|
||||
container
|
||||
lg:flex lg:items-center lg:justify-between
|
||||
space-y-4
|
||||
lg:space-y-0
|
||||
"
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<h1 class="font-bold text-2xl inline-block">Capsule</h1>
|
||||
<small class="block">
|
||||
©{{ new Date().getFullYear() }} All rights reserved
|
||||
</small>
|
||||
</div>
|
||||
<ul class="lg:flex lg:items-center lg:space-x-5 space-y-2 lg:space-y-0">
|
||||
<li class="block lg:hidden">
|
||||
<g-link to="/">About Capsule</g-link>
|
||||
</li>
|
||||
<li class="block lg:hidden pb-4">
|
||||
<g-link to="/docs">Documentation</g-link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/clastix/capsule"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex space-x-2 hover:text-blue-400"
|
||||
><icon-github class="w-5 h-5" /> <span> Github </span></a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://twitter.com/clastixio"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex space-x-2 hover:text-blue-400"
|
||||
><icon-twitter class="w-5 h-5" /> <span>Twitter</span></a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://www.linkedin.com/company/clastix/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="flex space-x-2 hover:text-blue-400"
|
||||
><icon-linkedin class="w-5 h-5" /> <span>Linkedin</span></a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import IconGithub from "~/assets/icon/github.svg?inline";
|
||||
import IconTwitter from "~/assets/icon/twitter.svg?inline";
|
||||
import IconLinkedin from "~/assets/icon/linkedin.svg?inline";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
IconGithub,
|
||||
IconLinkedin,
|
||||
IconTwitter,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
404
docs/src/components/AppNavbar.vue
Normal file
@@ -0,0 +1,404 @@
|
||||
<template>
|
||||
<header class="py-3 lg:py-6 relative bg-gray-900 bg-opacity-50">
|
||||
<div class="container flex items-center lg:justify-between">
|
||||
<div class="w-full flex items-center space-x-4 lg:space-x-0">
|
||||
<div class="w-2/12 lg:hidden" v-if="type === 'doc'">
|
||||
<button
|
||||
class="
|
||||
lg:hidden
|
||||
relative
|
||||
z-30
|
||||
shadow-lg
|
||||
w-8
|
||||
h-8
|
||||
space-y-1
|
||||
inline-flex
|
||||
flex-col
|
||||
justify-center
|
||||
items-center
|
||||
outline-none
|
||||
focus:outline-none
|
||||
transition-all
|
||||
duration-200
|
||||
transform
|
||||
"
|
||||
@click="toggleMenu()"
|
||||
>
|
||||
<span
|
||||
class="
|
||||
inline-block
|
||||
w-6
|
||||
h-0.5
|
||||
rounded-sm
|
||||
transition-all
|
||||
duration-200
|
||||
bg-gray-100
|
||||
"
|
||||
:class="{
|
||||
'transform rotate-45 translate-y-1.5': menuOpened,
|
||||
}"
|
||||
></span>
|
||||
<span
|
||||
class="inline-block w-6 h-0.5 rounded-sm bg-gray-100"
|
||||
:class="{
|
||||
invisible: menuOpened,
|
||||
}"
|
||||
></span>
|
||||
<span
|
||||
class="
|
||||
inline-block
|
||||
w-6
|
||||
h-0.5
|
||||
rounded-sm
|
||||
transition-all
|
||||
duration-200
|
||||
bg-gray-100
|
||||
"
|
||||
:class="{
|
||||
'transform -rotate-45 -translate-y-1.5': menuOpened,
|
||||
}"
|
||||
></span>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
class="w-8/12 lg:w-auto text-center flex items-center space-x-16"
|
||||
:class="{
|
||||
'justify-center lg:justify-start': type === 'doc',
|
||||
}"
|
||||
>
|
||||
<g-link to="/" class="flex items-center space-x-4">
|
||||
<logo-capsule class="w-10 lg:w-12" />
|
||||
<h1 class="font-bold text-2xl lg:text-3xl inline-block">Capsule</h1>
|
||||
</g-link>
|
||||
<nav class="hidden lg:inline-block">
|
||||
<ul class="flex items-center font-medium space-x-5">
|
||||
<li class="hidden lg:block">
|
||||
<g-link to="/">About Capsule</g-link>
|
||||
</li>
|
||||
<li>
|
||||
<g-link to="/docs">Documentation</g-link>
|
||||
</li>
|
||||
<!-- <li class="group relative">
|
||||
main
|
||||
<ul
|
||||
class="
|
||||
py-2
|
||||
px-4
|
||||
absolute
|
||||
top-full
|
||||
hidden
|
||||
group-hover:block
|
||||
bg-gray-100
|
||||
text-gray-800
|
||||
rounded
|
||||
text-left
|
||||
"
|
||||
>
|
||||
<li>
|
||||
<a href="/" target="_blank" rel="noopener noreferrer">
|
||||
v1.5.0
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/" target="_blank" rel="noopener noreferrer">
|
||||
v1.6.0
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/" target="_blank" rel="noopener noreferrer">
|
||||
main
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li> -->
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="w-2/12 lg:hidden" v-if="type === 'doc'">
|
||||
<button @click="toggleSearch()" class="block ml-auto">
|
||||
<icon-search class="w-8" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="relative"
|
||||
:class="{
|
||||
'hidden lg:flex items-center space-x-16': type === 'doc',
|
||||
}"
|
||||
>
|
||||
<div v-if="type === 'doc'">
|
||||
<div class="relative">
|
||||
<icon-search
|
||||
class="w-8 absolute left-0 bottom-2 text-gray-100 opacity-80"
|
||||
/>
|
||||
|
||||
<input
|
||||
type="search"
|
||||
name="search"
|
||||
id="search"
|
||||
class="
|
||||
rounded-0
|
||||
pl-10
|
||||
pr-2
|
||||
py-2
|
||||
outline-none
|
||||
w-96
|
||||
bg-transparent
|
||||
border-b border-solid border-gray-100
|
||||
text-gray-100
|
||||
"
|
||||
placeholder="Search"
|
||||
@focus="focused = true"
|
||||
@blur="focusOut()"
|
||||
@input="query = $event.target.value"
|
||||
@change="query = $event.target.value"
|
||||
/>
|
||||
</div>
|
||||
<ul
|
||||
v-show="showResult"
|
||||
class="
|
||||
absolute
|
||||
left-0
|
||||
top-12
|
||||
z-20
|
||||
text-left
|
||||
bg-gray-900
|
||||
rounded-br rounded-bl
|
||||
text-sm
|
||||
min-w-96
|
||||
"
|
||||
>
|
||||
<li v-if="results.length === 0" class="px-4 py-2">
|
||||
No results for <span class="font-bold">{{ query }}</span
|
||||
>.
|
||||
</li>
|
||||
<li v-else v-for="result in results" :key="result.id">
|
||||
<g-link
|
||||
:to="result.item.path + result.item.anchor"
|
||||
class="block px-4 py-2 hover:bg-gray-700 hover:text-blue-400"
|
||||
>
|
||||
<span v-if="result.item.value === result.item.title">
|
||||
{{ result.item.value }}
|
||||
</span>
|
||||
|
||||
<span v-else class="flex items-center">
|
||||
{{ result.item.title }}
|
||||
<icon-arrow class="w-2 mx-2 transform -rotate-90" />
|
||||
<span class="font-normal opacity-75">
|
||||
{{ result.item.value }}
|
||||
</span>
|
||||
</span>
|
||||
</g-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul
|
||||
class="items-center"
|
||||
:class="{
|
||||
'hidden lg:flex': type === 'doc',
|
||||
flex: type === 'default',
|
||||
}"
|
||||
>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/clastix/capsule"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Github
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
container
|
||||
py-4
|
||||
absolute
|
||||
bg-gray-900
|
||||
inset-x-0
|
||||
top-0
|
||||
z-30
|
||||
shadow-xl
|
||||
rounded-br rounded-bl
|
||||
"
|
||||
v-if="searchOpened && type === 'doc'"
|
||||
>
|
||||
<div class="flex items-center space-x-4">
|
||||
<input
|
||||
type="search"
|
||||
name="search"
|
||||
id="search"
|
||||
class="w-full rounded p-2 outline-none text-gray-800"
|
||||
placeholder="Search"
|
||||
@focus="focused = true"
|
||||
@blur="focusOutMobile()"
|
||||
@input="query = $event.target.value"
|
||||
@change="query = $event.target.value"
|
||||
/>
|
||||
<button @click="toggleSearch()">Cancel</button>
|
||||
</div>
|
||||
<ul class="p-4 space-y-2" v-show="showResult">
|
||||
<li v-if="results.length === 0">
|
||||
No results for <span class="font-bold">{{ query }}</span
|
||||
>.
|
||||
</li>
|
||||
<li v-else v-for="result in results" :key="result.id">
|
||||
<g-link
|
||||
:to="result.item.path + result.item.anchor"
|
||||
class="hover:text-blue-400"
|
||||
>
|
||||
<span v-if="result.item.value === result.item.title">
|
||||
{{ result.item.value }}
|
||||
</span>
|
||||
|
||||
<span v-else class="flex items-center">
|
||||
{{ result.item.title }}
|
||||
<span class="block">
|
||||
<icon-arrow class="w-2 mx-2 transform -rotate-90" />
|
||||
</span>
|
||||
<span class="font-normal opacity-75">
|
||||
{{ result.item.value }}
|
||||
</span>
|
||||
</span>
|
||||
</g-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<static-query>
|
||||
query Search {
|
||||
allMarkdownPage{
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
path
|
||||
title
|
||||
headings {
|
||||
depth
|
||||
value
|
||||
anchor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</static-query>
|
||||
|
||||
<script>
|
||||
import Fuse from "fuse.js";
|
||||
|
||||
import IconSearch from "~/assets/icon/search.svg?inline";
|
||||
import IconGithub from "~/assets/icon/github.svg?inline";
|
||||
import IconTwitter from "~/assets/icon/twitter.svg?inline";
|
||||
import IconSlack from "~/assets/icon/slack.svg?inline";
|
||||
import IconArrow from "~/assets/icon/arrow.svg?inline";
|
||||
import LogoCapsule from "~/assets/logo.svg?inline";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: () => "default",
|
||||
validator: (value) => ["default", "doc"].includes(value),
|
||||
},
|
||||
},
|
||||
|
||||
components: {
|
||||
IconSearch,
|
||||
IconGithub,
|
||||
IconTwitter,
|
||||
IconSlack,
|
||||
IconArrow,
|
||||
LogoCapsule,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
menuOpened: false,
|
||||
searchOpened: false,
|
||||
query: "",
|
||||
focusIndex: -1,
|
||||
focused: false,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route: function () {
|
||||
if (this.menuOpened) {
|
||||
this.menuOpened = false;
|
||||
this.$emit("onToggleMenu", this.menuOpened);
|
||||
document.querySelector("body").classList.remove("overflow-hidden");
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
results() {
|
||||
const fuse = new Fuse(this.headings, {
|
||||
keys: ["value"],
|
||||
threshold: 0.25,
|
||||
});
|
||||
return fuse.search(this.query).slice(0, 15);
|
||||
},
|
||||
|
||||
headings() {
|
||||
let result = [];
|
||||
const allPages = this.$static.allMarkdownPage.edges.map(
|
||||
(edge) => edge.node
|
||||
);
|
||||
// Create the array of all headings of all pages.
|
||||
allPages.forEach((page) => {
|
||||
page.headings.forEach((heading) => {
|
||||
result.push({
|
||||
...heading,
|
||||
path: page.path,
|
||||
title: page.title,
|
||||
});
|
||||
});
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
showResult() {
|
||||
// Show results, if the input is focused and the query is not empty.
|
||||
return this.focused && this.query.length > 0;
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
toggleMenu() {
|
||||
this.menuOpened = !this.menuOpened;
|
||||
this.$emit("onToggleMenu", this.menuOpened);
|
||||
if (this.menuOpened) {
|
||||
document.querySelector("body").classList.add("overflow-hidden");
|
||||
} else {
|
||||
document.querySelector("body").classList.remove("overflow-hidden");
|
||||
}
|
||||
},
|
||||
|
||||
toggleSearch() {
|
||||
this.searchOpened = !this.searchOpened;
|
||||
},
|
||||
|
||||
focusOut() {
|
||||
const _this = this;
|
||||
setTimeout(function () {
|
||||
_this.focused = false;
|
||||
}, 200);
|
||||
},
|
||||
|
||||
focusOutMobile() {
|
||||
const _this = this;
|
||||
setTimeout(function () {
|
||||
_this.focused = false;
|
||||
_this.toggleSearch();
|
||||
}, 200);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
135
docs/src/components/AppSidebar.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<aside>
|
||||
<nav>
|
||||
<ul class="space-y-3 pl-4 lg:pl-0">
|
||||
<li
|
||||
v-for="section in $static.allSidebar.edges[0].node.sections"
|
||||
:key="section.id"
|
||||
>
|
||||
<h5
|
||||
class="text-2xl lg:text-xl font-bold mb-1"
|
||||
v-if="section.title !== ''"
|
||||
>
|
||||
{{ section.title }}
|
||||
</h5>
|
||||
<ul
|
||||
class="space-y-1.5"
|
||||
:class="{
|
||||
'pl-2': section.title !== '',
|
||||
}"
|
||||
>
|
||||
<template v-for="(item, index) in section.items">
|
||||
<li :key="item.id" v-if="item.title === ''">
|
||||
<g-link
|
||||
:to="item.path"
|
||||
class="
|
||||
block
|
||||
transition-transform
|
||||
duration-100
|
||||
transform
|
||||
hover:translate-x-1
|
||||
text-lg
|
||||
lg:text-base
|
||||
hover:text-blue-400
|
||||
"
|
||||
:class="{
|
||||
'js-index-link': item.path === '/docs/' && index === 0,
|
||||
}"
|
||||
>
|
||||
{{ item.label }}</g-link
|
||||
>
|
||||
</li>
|
||||
<template v-else>
|
||||
<li :key="item.id">
|
||||
<app-accordion>
|
||||
<template v-slot:title>
|
||||
<h6 class="font-semibold text-xl lg:text-lg mb-1">
|
||||
{{ item.title }}
|
||||
</h6>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
<ul class="space-y-1.5 pl-2">
|
||||
<li v-for="subItem in item.subItems" :key="subItem.id">
|
||||
<g-link
|
||||
:to="subItem.path"
|
||||
class="
|
||||
block
|
||||
transition-transform
|
||||
duration-100
|
||||
transform
|
||||
hover:translate-x-1
|
||||
text-lg
|
||||
lg:text-base
|
||||
hover:text-blue-400
|
||||
"
|
||||
>
|
||||
{{ subItem.label }}</g-link
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</app-accordion>
|
||||
</li>
|
||||
</template>
|
||||
</template>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<static-query>
|
||||
{
|
||||
allSidebar {
|
||||
edges{
|
||||
node{
|
||||
id
|
||||
sections {
|
||||
title
|
||||
items{
|
||||
title
|
||||
label
|
||||
path
|
||||
subItems {
|
||||
label
|
||||
path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</static-query>
|
||||
|
||||
<script>
|
||||
import AppAccordion from "~/components/AppAccordion.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AppAccordion,
|
||||
},
|
||||
watch: {
|
||||
$route: function () {
|
||||
if (process.isClient && this.$route.fullPath !== "/docs/") {
|
||||
setTimeout(function () {
|
||||
document.querySelector(".js-index-link").classList.remove("active");
|
||||
}, 80);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.$route.fullPath !== "/docs/") {
|
||||
document.querySelector(".js-index-link").classList.remove("active");
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.active {
|
||||
@apply text-blue-400 font-semibold;
|
||||
}
|
||||
</style>
|
||||
108
docs/src/components/OnThisPage.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<ul class="space-y-2">
|
||||
<template v-for="(heading, index) in headings">
|
||||
<li v-if="heading.depth > 1" :key="index" class="hover:underline">
|
||||
<g-link
|
||||
:to="`${pagePath}${heading.anchor}`"
|
||||
class="text-sm"
|
||||
:class="{
|
||||
'text-white underline font-semibold':
|
||||
activeAnchor === heading.anchor,
|
||||
'text-gray-300': activeAnchor !== heading.anchor,
|
||||
}"
|
||||
>
|
||||
{{ heading.value }}
|
||||
</g-link>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
headings: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
pagePath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
activeAnchor: "",
|
||||
observer: null,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
$route: function () {
|
||||
if (process.isClient && window.location.hash) {
|
||||
this.activeAnchor = window.location.hash;
|
||||
}
|
||||
|
||||
if (this.observer) {
|
||||
// Clear the current observer.
|
||||
this.observer.disconnect();
|
||||
|
||||
this.$nextTick(this.initObserver);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (process.isClient) {
|
||||
if (window.location.hash) {
|
||||
this.activeAnchor = window.location.hash;
|
||||
}
|
||||
this.$nextTick(this.initObserver);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
observerCallback(entries, observer) {
|
||||
// This early return fixes the jumping
|
||||
// of the bubble active state when we click on a link.
|
||||
// There should be only one intersecting element anyways.
|
||||
if (entries.length > 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const id = entries[0].target.id;
|
||||
|
||||
// We want to give the link of the intersecting
|
||||
// headline active and add the hash to the url.
|
||||
if (id) {
|
||||
this.activeAnchor = "#" + id;
|
||||
|
||||
if (history.replaceState) {
|
||||
history.replaceState(null, null, "#" + id);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initObserver() {
|
||||
this.observer = new IntersectionObserver(this.observerCallback, {
|
||||
// This rootMargin should allow intersections at the top of the page.
|
||||
// root: document.querySelector('.content'),
|
||||
rootMargin: "0px 0px 99999px",
|
||||
threshold: 1,
|
||||
});
|
||||
|
||||
const elements = document.querySelectorAll(
|
||||
".content h2, .content h3, .content h4, .content h5, .content h6"
|
||||
);
|
||||
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
this.observer.observe(elements[i]);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
4
docs/src/components/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
Add components that will be imported to Pages and Layouts to this folder.
|
||||
Learn more about components here: https://gridsome.org/docs/components/
|
||||
|
||||
You can delete this file.
|
||||
41
docs/src/layouts/Default.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="bg-gray-800 text-gray-100 relative">
|
||||
<app-navbar @onToggleMenu="toggleMenu" class="mb-16" />
|
||||
<div
|
||||
class="
|
||||
relative
|
||||
lg:flex lg:items-stretch lg:space-x-12
|
||||
min-h-screen
|
||||
whitespace-normal
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
<app-footer />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AppNavbar from "~/components/AppNavbar.vue";
|
||||
import AppFooter from "~/components/AppFooter.vue";
|
||||
import AppSidebar from "~/components/AppSidebar.vue";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AppNavbar,
|
||||
AppFooter,
|
||||
AppSidebar,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isMenuOpened: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleMenu(value) {
|
||||
this.isMenuOpened = value;
|
||||
console.log(this.isMenuOpened);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||