mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-03-23 03:49:18 +00:00
Compare commits
14 Commits
helm-v0.1.
...
helm-v0.1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
260b60d263 | ||
|
|
e0d5e6feb2 | ||
|
|
0784dc7177 | ||
|
|
b17c6c4636 | ||
|
|
52cf597041 | ||
|
|
b8dcded882 | ||
|
|
6a175e9017 | ||
|
|
3c609f84db | ||
|
|
7c3a59c4e4 | ||
|
|
d3e3b8a881 | ||
|
|
7a8148bd58 | ||
|
|
405d3ac52d | ||
|
|
f92acf9a9d | ||
|
|
bbb7b850d6 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,6 +22,7 @@ bin
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.vscode
|
||||
|
||||
**/*.kubeconfig
|
||||
**/*.crt
|
||||
|
||||
53
Makefile
53
Makefile
@@ -45,7 +45,7 @@ manager: generate fmt vet
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
run: generate manifests
|
||||
go run ./main.go
|
||||
go run .
|
||||
|
||||
# Creates the single file to install Capsule without any external dependency
|
||||
installer: manifests kustomize
|
||||
@@ -78,6 +78,57 @@ manifests: controller-gen
|
||||
generate: controller-gen
|
||||
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
|
||||
|
||||
# Setup development env
|
||||
# Usage:
|
||||
# LAPTOP_HOST_IP=<YOUR_LAPTOP_IP> make dev-setup
|
||||
# For example:
|
||||
# LAPTOP_HOST_IP=192.168.10.101 make dev-setup
|
||||
define TLS_CNF
|
||||
[ req ]
|
||||
default_bits = 4096
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = req_ext
|
||||
[ req_distinguished_name ]
|
||||
countryName = SG
|
||||
stateOrProvinceName = SG
|
||||
localityName = SG
|
||||
organizationName = CAPSULE
|
||||
commonName = CAPSULE
|
||||
[ req_ext ]
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = $(LAPTOP_HOST_IP)
|
||||
endef
|
||||
export TLS_CNF
|
||||
dev-setup:
|
||||
kubectl -n capsule-system scale deployment capsule-controller-manager --replicas=0
|
||||
mkdir -p /tmp/k8s-webhook-server/serving-certs
|
||||
echo "$${TLS_CNF}" > _tls.cnf
|
||||
openssl req -newkey rsa:4096 -days 3650 -nodes -x509 \
|
||||
-subj "/C=SG/ST=SG/L=SG/O=CAPSULE/CN=CAPSULE" \
|
||||
-extensions req_ext \
|
||||
-config _tls.cnf \
|
||||
-keyout /tmp/k8s-webhook-server/serving-certs/tls.key \
|
||||
-out /tmp/k8s-webhook-server/serving-certs/tls.crt
|
||||
rm -f _tls.cnf
|
||||
export WEBHOOK_URL="https://$${LAPTOP_HOST_IP}:9443"; \
|
||||
export CA_BUNDLE=`openssl base64 -in /tmp/k8s-webhook-server/serving-certs/tls.crt | tr -d '\n'`; \
|
||||
kubectl patch MutatingWebhookConfiguration capsule-mutating-webhook-configuration \
|
||||
--type='json' -p="[\
|
||||
{'op': 'replace', 'path': '/webhooks/0/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/mutate-v1-namespace-owner-reference\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
]" && \
|
||||
kubectl patch ValidatingWebhookConfiguration capsule-validating-webhook-configuration \
|
||||
--type='json' -p="[\
|
||||
{'op': 'replace', 'path': '/webhooks/0/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/cordoning\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/1/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/ingresses\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/2/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/namespaces\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/3/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/networkpolicies\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/4/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/pods\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/5/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/persistentvolumeclaims\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/6/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/services\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/7/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/tenants\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
]";
|
||||
|
||||
# Build the docker image
|
||||
docker-build: test
|
||||
docker build . -t ${IMG} --build-arg GIT_HEAD_COMMIT=$(GIT_HEAD_COMMIT) \
|
||||
|
||||
17
README.md
17
README.md
@@ -150,9 +150,6 @@ Error from server (Forbidden): pods is forbidden:
|
||||
User "bob" cannot list resource "pods" in API group "" in the namespace "kube-system"
|
||||
```
|
||||
|
||||
# Documentation
|
||||
Please, check the project [documentation](./docs/index.md) for more cool things you can do with Capsule.
|
||||
|
||||
# Removal
|
||||
Similar to `deploy`, you can get rid of Capsule using the `remove` target.
|
||||
|
||||
@@ -160,15 +157,21 @@ Similar to `deploy`, you can get rid of Capsule using the `remove` target.
|
||||
$ make remove
|
||||
```
|
||||
|
||||
# Documentation
|
||||
Please, check the project [documentation](./docs/index.md) for more cool things you can do with Capsule.
|
||||
|
||||
# Contribution
|
||||
Capsule is Open Source with Apache 2 license and any contribution is welcome.
|
||||
|
||||
Please refer to the corresponding docs:
|
||||
- [contributing.md](./docs/contributing.md) for the general guide; and
|
||||
- [dev-guide.md](./docs/dev-guide.md) for how to set up the development env to get started.
|
||||
|
||||
# FAQ
|
||||
- Q. How to pronounce Capsule?
|
||||
|
||||
A. It should be pronounced as `/ˈkæpsjuːl/`.
|
||||
|
||||
- Q. Can I contribute?
|
||||
|
||||
A. Absolutely! Capsule is Open Source with Apache 2 license and any contribution is welcome. Please refer to the corresponding [section](./docs/operator/contributing.md) in the documentation.
|
||||
|
||||
- Q. Is it production grade?
|
||||
|
||||
A. Although under frequent development and improvements, Capsule is ready to be used in production environments as currently, people are using it in public and private deployments. Check out the [release](https://github.com/clastix/capsule/releases) page for a detailed list of available versions.
|
||||
|
||||
@@ -21,7 +21,7 @@ sources:
|
||||
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
version: 0.1.2
|
||||
version: 0.1.3
|
||||
|
||||
# This is the version number of the application being deployed.
|
||||
# This version number should be incremented each time you make changes to the application.
|
||||
|
||||
@@ -163,7 +163,7 @@ webhooks:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: capsule-system
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /persistentvolumeclaims
|
||||
failurePolicy: {{ .Values.webhooks.persistentvolumeclaims.failurePolicy }}
|
||||
name: pvc.capsule.clastix.io
|
||||
|
||||
BIN
docs/assets/dev-env.png
Normal file
BIN
docs/assets/dev-env.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
63
docs/contributing.md
Normal file
63
docs/contributing.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# How to contribute to Capsule
|
||||
|
||||
First, thanks for your interest in Capsule, any contribution is welcome!
|
||||
|
||||
## Development environment setup
|
||||
|
||||
The first step is to set up your local development environment.
|
||||
|
||||
Please follow the [Capsule Development Guide](dev-guide.md) for details.
|
||||
|
||||
## Code convention
|
||||
|
||||
The changes must follow the Pull Request method where a _GitHub Action_ will
|
||||
check the `golangci-lint`, so ensure your changes respect the coding standard.
|
||||
|
||||
### golint
|
||||
|
||||
You can easily check them issuing the _Make_ recipe `golint`.
|
||||
|
||||
```
|
||||
# make golint
|
||||
golangci-lint run -c .golangci.yml
|
||||
```
|
||||
|
||||
> Enabled linters and related options are defined in the [.golanci.yml file](../../.golangci.yml)
|
||||
|
||||
### goimports
|
||||
|
||||
Also, the Go import statements must be sorted following the best practice:
|
||||
|
||||
```
|
||||
<STANDARD LIBRARY>
|
||||
|
||||
<EXTERNAL PACKAGES>
|
||||
|
||||
<LOCAL PACKAGES>
|
||||
```
|
||||
|
||||
To help you out you can use the _Make_ recipe `goimports`
|
||||
|
||||
```
|
||||
# make goimports
|
||||
goimports -w -l -local "github.com/clastix/capsule" .
|
||||
```
|
||||
|
||||
### Commits
|
||||
|
||||
All the Pull Requests must refer to an already open issue: this is the first phase to contribute also for informing maintainers about the issue.
|
||||
|
||||
Commit's first line should not exceed 50 columns.
|
||||
|
||||
A commit description is welcomed to explain more the changes: just ensure
|
||||
to put a blank line and an arbitrary number of maximum 72 characters long
|
||||
lines, at most one blank line between them.
|
||||
|
||||
Please, split changes into several and documented small commits: this will help us to perform a better review. Commits must follow the Conventional Commits Specification, a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of. This convention dovetails with Semantic Versioning, by describing the features, fixes, and breaking changes made in commit messages. See [Conventional Commits Specification](https://www.conventionalcommits.org) to learn about Conventional Commits.
|
||||
|
||||
> In case of errors or need of changes to previous commits,
|
||||
> fix them squashing to make changes atomic.
|
||||
|
||||
### Miscellanea
|
||||
|
||||
Please, add a new single line at end of any file as the current coding style.
|
||||
359
docs/dev-guide.md
Normal file
359
docs/dev-guide.md
Normal file
@@ -0,0 +1,359 @@
|
||||
# Capsule Development Guide
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Tools
|
||||
|
||||
Make sure you have these tools installed:
|
||||
|
||||
- [Go 1.16+](https://golang.org/dl/)
|
||||
- [Operator SDK 1.7.2+](https://github.com/operator-framework/operator-sdk), or [Kubebuilder](https://github.com/kubernetes-sigs/kubebuilder)
|
||||
- [KinD](https://github.com/kubernetes-sigs/kind) or [k3d](https://k3d.io/), with `kubectl`
|
||||
- [ngrok](https://ngrok.com/) (if you want to run locally with remote Kubernetes)
|
||||
- [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
- OpenSSL
|
||||
|
||||
### Kubernetes Cluster
|
||||
|
||||
A lightweight Kubernetes within your laptop can be very handy for Kubernetes-native development like Capsule.
|
||||
|
||||
#### By `k3d`
|
||||
|
||||
```sh
|
||||
# Install K3d cli by brew in Mac, or your preferred way
|
||||
$ brew install k3d
|
||||
|
||||
# Export your laptop's IP, e.g. retrieving it by: ifconfig
|
||||
# Do change this IP to yours
|
||||
$ export LAPTOP_HOST_IP=192.168.10.101
|
||||
|
||||
# Spin up a bare minimum cluster
|
||||
# Refer to here for more options: https://k3d.io/v4.4.8/usage/commands/k3d_cluster_create/
|
||||
$ k3d cluster create k3s-capsule --servers 1 --agents 1 --no-lb --k3s-server-arg --tls-san=${LAPTOP_HOST_IP}
|
||||
|
||||
# This will create a cluster with 1 server and 1 worker node
|
||||
$ kubectl get nodes
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
k3d-k3s-capsule-server-0 Ready control-plane,master 2m13s v1.21.2+k3s1
|
||||
k3d-k3s-capsule-agent-0 Ready <none> 2m3s v1.21.2+k3s1
|
||||
|
||||
# Or 2 Docker containers if you view it from Docker perspective
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
5c26ad840c62 rancher/k3s:v1.21.2-k3s1 "/bin/k3s agent" 53 seconds ago Up 45 seconds k3d-k3s-capsule-agent-0
|
||||
753998879b28 rancher/k3s:v1.21.2-k3s1 "/bin/k3s server --t…" 53 seconds ago Up 51 seconds 0.0.0.0:49708->6443/tcp k3d-k3s-capsule-server-0
|
||||
```
|
||||
|
||||
#### By `kind`
|
||||
|
||||
```sh
|
||||
# # Install kind cli by brew in Mac, or your preferred way
|
||||
$ brew install kind
|
||||
|
||||
# Prepare a kind config file with necessary customization
|
||||
$ cat > kind.yaml <<EOF
|
||||
kind: Cluster
|
||||
apiVersion: kind.x-k8s.io/v1alpha4
|
||||
networking:
|
||||
apiServerAddress: "0.0.0.0"
|
||||
nodes:
|
||||
- role: control-plane
|
||||
kubeadmConfigPatches:
|
||||
- |
|
||||
kind: ClusterConfiguration
|
||||
metadata:
|
||||
name: config
|
||||
apiServer:
|
||||
certSANs:
|
||||
- localhost
|
||||
- 127.0.0.1
|
||||
- kubernetes
|
||||
- kubernetes.default.svc
|
||||
- kubernetes.default.svc.cluster.local
|
||||
- kind
|
||||
- 0.0.0.0
|
||||
- ${LAPTOP_HOST_IP}
|
||||
- role: worker
|
||||
EOF
|
||||
|
||||
# Spin up a bare minimum cluster with 1 master 1 worker node
|
||||
$ kind create cluster --name kind-capsule --config kind.yaml
|
||||
|
||||
# This will create a cluster with 1 server and 1 worker node
|
||||
$ kubectl get nodes
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
kind-capsule-control-plane Ready control-plane,master 84s v1.21.1
|
||||
kind-capsule-worker Ready <none> 56s v1.21.1
|
||||
|
||||
# Or 2 Docker containers if you view it from Docker perspective
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
7b329fd3a838 kindest/node:v1.21.1 "/usr/local/bin/entr…" About a minute ago Up About a minute 0.0.0.0:54894->6443/tcp kind-capsule-control-plane
|
||||
7d50f1633555 kindest/node:v1.21.1 "/usr/local/bin/entr…" About a minute ago Up About a minute kind-capsule-worker
|
||||
```
|
||||
|
||||
## Fork & clone the repository
|
||||
|
||||
The `fork-clone-contribute-pr` flow is common for contributing to OSS projects like Kubernetes, Capsule.
|
||||
|
||||
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
|
||||
$ 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
|
||||
$ git remote add upstream https://github.com/clastix/capsule.git
|
||||
$ git remote -vv
|
||||
origin git@github.com:myuser/capsule.git (fetch)
|
||||
origin git@github.com:myuser/capsule.git (push)
|
||||
upstream https://github.com/clastix/capsule.git (fetch)
|
||||
upstream https://github.com/clastix/capsule.git (push)
|
||||
```
|
||||
|
||||
## Build & deploy Capsule
|
||||
|
||||
```sh
|
||||
# Download the project dependencies
|
||||
$ go mod download
|
||||
|
||||
# Build the Capsule image
|
||||
$ make docker-build
|
||||
|
||||
# Retrieve the built image version
|
||||
$ export CAPSULE_IMAGE_VESION=`docker images --format '{{.Tag}}' quay.io/clastix/capsule`
|
||||
|
||||
# If k3s, load the image into cluster by
|
||||
$ k3d image import --cluster k3s-capsule capsule quay.io/clastix/capsule:${CAPSULE_IMAGE_VESION}
|
||||
# If Kind, load the image into cluster by
|
||||
$ kind load docker-image --name kind-capsule quay.io/clastix/capsule:${CAPSULE_IMAGE_VESION}
|
||||
|
||||
# deploy all the required manifests
|
||||
# Note: 1) please retry if you saw errors; 2) if you want to clean it up first, run: make remove
|
||||
$ make deploy
|
||||
|
||||
# Make sure the controller is running
|
||||
$ kubectl get pod -n capsule-system
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
capsule-controller-manager-5c6b8445cf-566dc 1/1 Running 0 23s
|
||||
|
||||
# Check the logs if needed
|
||||
$ kubectl -n capsule-system logs --all-containers -l control-plane=controller-manager
|
||||
|
||||
# You may have a try to deploy a Tenant too to make sure it works end to end
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: capsule.clastix.io/v1beta1
|
||||
kind: Tenant
|
||||
metadata:
|
||||
name: oil
|
||||
spec:
|
||||
owners:
|
||||
- name: alice
|
||||
kind: User
|
||||
EOF
|
||||
|
||||
# There shouldn't be any errors and you should see the newly created tenant
|
||||
$ kubectl get tenants
|
||||
NAME STATE NAMESPACE QUOTA NAMESPACE COUNT NODE SELECTOR AGE
|
||||
oil Active 0 14s
|
||||
```
|
||||
|
||||
As of now, a complete Capsule environment has been set up in `kind`- or `k3d`-powered cluster, and the `capsule-controller-manager` is running as a deployment serving as:
|
||||
|
||||
- The reconcilers for CRDs and;
|
||||
- A series of webhooks
|
||||
|
||||
|
||||
## Set up development env
|
||||
|
||||
During development, we prefer that the code is running within our IDE locally, instead of running as the normal Pod(s) within the Kubernetes cluster.
|
||||
|
||||
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
|
||||
# If you haven't installed or run `make deploy` before, do it first
|
||||
# Note: please retry if you saw errors
|
||||
$ make deploy
|
||||
|
||||
# To retrieve your laptop's IP and execute `make dev-setup` to setup dev env
|
||||
# For example: LAPTOP_HOST_IP=192.168.10.101 make dev-setup
|
||||
$ LAPTOP_HOST_IP="<YOUR_LAPTOP_IP>" make dev-setup
|
||||
```
|
||||
|
||||
|
||||
This is a very common setup for typical Kubernetes Operator development so we'd better walk them through with more details here.
|
||||
|
||||
1. Scaling down the deployed Pod(s) to 0
|
||||
|
||||
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
|
||||
$ kubectl -n capsule-system scale deployment capsule-controller-manager --replicas=0
|
||||
deployment.apps/capsule-controller-manager scaled
|
||||
```
|
||||
|
||||
2. Preparing TLS certificate for the webhooks
|
||||
|
||||
Running webhooks requires TLS, we can prepare the TLS key pair in our development env to handle HTTPS requests.
|
||||
|
||||
```sh
|
||||
# Prepare a simple OpenSSL config file
|
||||
# Do remember to export LAPTOP_HOST_IP before running this command
|
||||
$ cat > _tls.cnf <<EOF
|
||||
[ req ]
|
||||
default_bits = 4096
|
||||
distinguished_name = req_distinguished_name
|
||||
req_extensions = req_ext
|
||||
[ req_distinguished_name ]
|
||||
countryName = SG
|
||||
stateOrProvinceName = SG
|
||||
localityName = SG
|
||||
organizationName = CAPSULE
|
||||
commonName = CAPSULE
|
||||
[ req_ext ]
|
||||
subjectAltName = @alt_names
|
||||
[alt_names]
|
||||
IP.1 = ${LAPTOP_HOST_IP}
|
||||
EOF
|
||||
|
||||
# Create this dir to mimic the Pod mount point
|
||||
$ mkdir -p /tmp/k8s-webhook-server/serving-certs
|
||||
|
||||
# Generate the TLS cert/key under /tmp/k8s-webhook-server/serving-certs
|
||||
$ openssl req -newkey rsa:4096 -days 3650 -nodes -x509 \
|
||||
-subj "/C=SG/ST=SG/L=SG/O=CAPSULE/CN=CAPSULE" \
|
||||
-extensions req_ext \
|
||||
-config _tls.cnf \
|
||||
-keyout /tmp/k8s-webhook-server/serving-certs/tls.key \
|
||||
-out /tmp/k8s-webhook-server/serving-certs/tls.crt
|
||||
|
||||
# Clean it up
|
||||
$ rm -f _tls.cnf
|
||||
```
|
||||
|
||||
3. Patching the Webhooks
|
||||
|
||||
By default, the webhooks will be registered with the services, which will route to the Pods, inside the cluster.
|
||||
|
||||
We need to _delegate_ the controllers' and webbooks' services to the code running in our IDE by patching the `MutatingWebhookConfiguration` and `ValidatingWebhookConfiguration`.
|
||||
|
||||
```sh
|
||||
# Export your laptop's IP with the 9443 port exposed by controllers/webhooks' services
|
||||
$ export WEBHOOK_URL="https://${LAPTOP_HOST_IP}:9443"
|
||||
|
||||
# Export the cert we just generated as the CA bundle for webhook TLS
|
||||
$ export CA_BUNDLE=`openssl base64 -in /tmp/k8s-webhook-server/serving-certs/tls.crt | tr -d '\n'`
|
||||
|
||||
# Patch the MutatingWebhookConfiguration webhook
|
||||
$ kubectl patch MutatingWebhookConfiguration capsule-mutating-webhook-configuration \
|
||||
--type='json' -p="[\
|
||||
{'op': 'replace', 'path': '/webhooks/0/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/mutate-v1-namespace-owner-reference\",'caBundle':\"${CA_BUNDLE}\"}}\
|
||||
]"
|
||||
|
||||
# Verify it if you want
|
||||
$ kubectl get MutatingWebhookConfiguration capsule-mutating-webhook-configuration -o yaml
|
||||
|
||||
# Patch the ValidatingWebhookConfiguration webhooks
|
||||
# Note: there is a list of validating webhook endpoints, not just one
|
||||
$ kubectl patch ValidatingWebhookConfiguration capsule-validating-webhook-configuration \
|
||||
--type='json' -p="[\
|
||||
{'op': 'replace', 'path': '/webhooks/0/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/cordoning\",'caBundle':\"${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/1/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/ingresses\",'caBundle':\"${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/2/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/namespaces\",'caBundle':\"${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/3/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/networkpolicies\",'caBundle':\"${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/4/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/pods\",'caBundle':\"${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/5/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/persistentvolumeclaims\",'caBundle':\"${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/6/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/services\",'caBundle':\"${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/7/clientConfig', 'value':{'url':\"${WEBHOOK_URL}/tenants\",'caBundle':\"${CA_BUNDLE}\"}}\
|
||||
]"
|
||||
|
||||
# Verify it if you want
|
||||
$ kubectl get ValidatingWebhookConfiguration capsule-validating-webhook-configuration -o yaml
|
||||
```
|
||||
|
||||
## Run Capsule outside the cluster
|
||||
|
||||
Now we can run Capsule controllers with webhooks outside of the Kubernetes cluster:
|
||||
|
||||
```sh
|
||||
$ export NAMESPACE=capsule-system && export TMPDIR=/tmp/
|
||||
$ go run .
|
||||
```
|
||||
|
||||
To verify that, we can open a new console and create a new Tenant:
|
||||
|
||||
```sh
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: capsule.clastix.io/v1beta1
|
||||
kind: Tenant
|
||||
metadata:
|
||||
name: gas
|
||||
spec:
|
||||
owners:
|
||||
- name: alice
|
||||
kind: User
|
||||
EOF
|
||||
```
|
||||
|
||||
We should see output like:
|
||||
```log
|
||||
tenant.capsule.clastix.io/gas created
|
||||
```
|
||||
|
||||
And could see logs in the `make run` console like:
|
||||
```log
|
||||
...
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.520+0800","logger":"controllers.Tenant","msg":"Ensuring all Namespaces are collected","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.527+0800","logger":"controllers.Tenant","msg":"Starting processing of Namespaces","Request.Name":"gas","items":0}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.527+0800","logger":"controllers.Tenant","msg":"Ensuring additional RoleBindings for owner","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.527+0800","logger":"controllers.Tenant","msg":"Ensuring RoleBinding for owner","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.527+0800","logger":"controllers.Tenant","msg":"Ensuring Namespace count","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.533+0800","logger":"controllers.Tenant","msg":"Tenant reconciling completed","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.540+0800","logger":"controllers.Tenant","msg":"Ensuring all Namespaces are collected","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.547+0800","logger":"controllers.Tenant","msg":"Starting processing of Namespaces","Request.Name":"gas","items":0}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.547+0800","logger":"controllers.Tenant","msg":"Ensuring additional RoleBindings for owner","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.547+0800","logger":"controllers.Tenant","msg":"Ensuring RoleBinding for owner","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.547+0800","logger":"controllers.Tenant","msg":"Ensuring Namespace count","Request.Name":"gas"}
|
||||
{"level":"info","ts":"2021-09-28T21:10:30.554+0800","logger":"controllers.Tenant","msg":"Tenant reconciling completed","Request.Name":"gas"}
|
||||
```
|
||||
|
||||
## Work in your preferred IDE
|
||||
|
||||
Now it's time to work through our familiar inner loop for development in our preferred IDE.
|
||||
|
||||
For example, if you're using [Visual Studio Code](https://code.visualstudio.com), this `launch.json` file can be a good start.
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}",
|
||||
"args": [
|
||||
"--zap-encoder=console",
|
||||
"--zap-log-level=debug",
|
||||
"--configuration-name=capsule-default"
|
||||
],
|
||||
"env": {
|
||||
"NAMESPACE": "capsule-system",
|
||||
"TMPDIR": "/tmp/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Please refer to [contributing.md](contributing.md) for more details while contributing.
|
||||
@@ -1,251 +0,0 @@
|
||||
# How to contribute to Capsule
|
||||
First, thanks for your interest in Capsule, any contribution is welcome!
|
||||
|
||||
The first step is to set up your local development environment as stated below:
|
||||
|
||||
## Setting up the development environment
|
||||
The following dependencies are mandatory:
|
||||
|
||||
- [Go 1.16](https://golang.org/dl/)
|
||||
- [OperatorSDK 1.7.2](https://github.com/operator-framework/operator-sdk)
|
||||
- [Kubebuilder](https://github.com/kubernetes-sigs/kubebuilder)
|
||||
- [KinD](https://github.com/kubernetes-sigs/kind)
|
||||
- [ngrok](https://ngrok.com/) (if you want to run locally)
|
||||
- [golangci-lint](https://github.com/golangci/golangci-lint)
|
||||
|
||||
### Installing Go dependencies
|
||||
After cloning Capsule on any folder, access it and issue the following command
|
||||
to ensure all dependencies are properly downloaded.
|
||||
|
||||
```
|
||||
go mod download
|
||||
```
|
||||
|
||||
### Installing Operator SDK
|
||||
Some operations, like the Docker image build process or the code-generation of
|
||||
the CRDs manifests, as well the deep copy functions, require _Operator SDK_:
|
||||
the binary has to be installed into your `PATH`.
|
||||
|
||||
### Installing Kubebuilder
|
||||
With the latest release of OperatorSDK there's a more tighten integration with
|
||||
Kubebuilder and its opinionated testing suite: ensure to download the latest
|
||||
binaries available from the _Releases_ GitHub page and place them into the
|
||||
`/usr/local/kubebuilder/bin` folder, ensuring this is also in your `PATH`.
|
||||
|
||||
### Installing KinD
|
||||
Capsule can run on any certified Kubernetes installation and locally
|
||||
the whole development is performed on _KinD_, also knows as
|
||||
[Kubernetes in Docker](https://github.com/kubernetes-sigs/kind).
|
||||
|
||||
> N.B.: Docker is a hard requirement since it's based on it
|
||||
|
||||
According to your operative system and architecture, download the right binary
|
||||
and place it on your `PATH`.
|
||||
|
||||
Once done, you're ready to bootstrap in a glance of seconds, a fully functional
|
||||
Kubernetes cluster.
|
||||
|
||||
```
|
||||
# kind create cluster --name capsule
|
||||
Creating cluster "capsule" ...
|
||||
✓ Ensuring node image (kindest/node:v1.18.2) 🖼
|
||||
✓ Preparing nodes 📦
|
||||
✓ Writing configuration 📜
|
||||
✓ Starting control-plane 🕹️
|
||||
✓ Installing CNI 🔌
|
||||
✓ Installing StorageClass 💾
|
||||
Set kubectl context to "kind-capsule"
|
||||
You can now use your cluster with:
|
||||
|
||||
kubectl cluster-info --context kind-capsule
|
||||
|
||||
Thanks for using kind! 😊
|
||||
```
|
||||
|
||||
The current `KUBECONFIG` will be populated with the `cluster-admin`
|
||||
certificates and the context changed to the just born Kubernetes cluster.
|
||||
|
||||
### Build the Docker image and push it to KinD
|
||||
From the root path, issue the _make_ recipe:
|
||||
|
||||
```
|
||||
# make docker-build
|
||||
```
|
||||
|
||||
The image `quay.io/clastix/capsule:<tag>` will be available locally. Built image `<tag>` is resulting last one available [release](https://github.com/clastix/capsule/releases).
|
||||
|
||||
Push it to _KinD_ with the following command:
|
||||
|
||||
```
|
||||
# kind load docker-image --nodes capsule-control-plane --name capsule quay.io/clastix/capsule:<tag>
|
||||
```
|
||||
|
||||
### Deploy the Kubernetes manifests
|
||||
With the current `kind-capsule` context enabled, deploy all the required
|
||||
manifests issuing the following command:
|
||||
|
||||
```
|
||||
make deploy
|
||||
```
|
||||
|
||||
This will install all the required Kubernetes resources, automatically.
|
||||
|
||||
You can check if Capsule is running tailing the logs:
|
||||
|
||||
```
|
||||
# kubectl -n capsule-system logs --all-containers -f -l control-plane=controller-manager
|
||||
```
|
||||
|
||||
Since Capsule is built using _OperatorSDK_, logging is handled by the zap
|
||||
module: log verbosity of the Capsule controller can be increased passing
|
||||
the `--zap-log-level` option with a value from `1` to `10` or the
|
||||
[basic keywords](https://godoc.org/go.uber.org/zap/zapcore#Level) although
|
||||
it is suggested to use the `--zap-devel` flag to get also stack traces.
|
||||
|
||||
> CA generation
|
||||
>
|
||||
> You could notice a restart of the Capsule pod upon installation, that's ok:
|
||||
> Capsule is generating the CA and populating the Secret containing the TLS
|
||||
> certificate to handle the webhooks and there's the need the reload the whole
|
||||
> application to serve properly HTTPS requests.
|
||||
|
||||
### Run Capsule locally
|
||||
Debugging remote applications is always struggling but Operators just need
|
||||
access to the Kubernetes API Server.
|
||||
|
||||
#### Scaling down the remote Pod
|
||||
First, ensure the Capsule pod is not running scaling down the Deployment.
|
||||
|
||||
```
|
||||
# kubectl -n capsule-system scale deployment capsule-controller-manager --replicas=0
|
||||
deployment.apps/capsule-controller-manager scaled
|
||||
```
|
||||
|
||||
> This is mandatory since Capsule uses Leader Election
|
||||
|
||||
#### Providing TLS certificate for webhooks
|
||||
The next step is to replicate the same environment Capsule is expecting in the Pod,
|
||||
it means creating a fake certificate to handle HTTP requests.
|
||||
|
||||
``` bash
|
||||
mkdir -p /tmp/k8s-webhook-server/serving-certs
|
||||
kubectl -n capsule-system get secret capsule-tls -o jsonpath='{.data.tls\.crt}' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.crt
|
||||
kubectl -n capsule-system get secret capsule-tls -o jsonpath='{.data.tls\.key}' | base64 -d > /tmp/k8s-webhook-server/serving-certs/tls.key
|
||||
```
|
||||
|
||||
> We're using the certificates generate upon the first installation of Capsule:
|
||||
> it means the Secret will be populated at the first start-up.
|
||||
> If you plan to run it locally since the beginning, it means you will require
|
||||
> to provide a self-signed certificate in the said directory.
|
||||
|
||||
#### Starting NGROK
|
||||
In another session, we need a `ngrok` session, mandatory to debug also webhooks
|
||||
(YMMV).
|
||||
|
||||
```
|
||||
# ngrok http https://localhost:9443
|
||||
ngrok by @inconshreveable
|
||||
|
||||
Session Status online
|
||||
Account Dario Tranchitella (Plan: Free)
|
||||
Version 2.3.35
|
||||
Region United States (us)
|
||||
Web Interface http://127.0.01:4040
|
||||
Forwarding http://cdb72b99348c.ngrok.io -> https://localhost:9443
|
||||
Forwarding https://cdb72b99348c.ngrok.io -> https://localhost:9443
|
||||
Connections ttl opn rt1 rt5 p50 p90
|
||||
0 0 0.00 0.00 0.00 0.00
|
||||
```
|
||||
|
||||
What we need is the _ngrok_ URL (in this case, `https://cdb72b99348c.ngrok.io`)
|
||||
since we're going to use this default URL as the `url` parameter for the
|
||||
_Dynamic Admissions Control Webhooks_.
|
||||
|
||||
#### Patching the MutatingWebhookConfiguration
|
||||
Now it's time to patch the _MutatingWebhookConfiguration_ and the
|
||||
_ValidatingWebhookConfiguration_ too, adding the said `ngrok` URL as base for
|
||||
each defined webhook, as following:
|
||||
|
||||
```diff
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: MutatingWebhookConfiguration
|
||||
metadata:
|
||||
|
||||
name: capsule-mutating-webhook-configuration
|
||||
webhooks:
|
||||
- name: owner.namespace.capsule.clastix.io
|
||||
failurePolicy: Fail
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
apiVersions: ["v1"]
|
||||
operations: ["CREATE"]
|
||||
resources: ["namespaces"]
|
||||
clientConfig:
|
||||
+ url: https://cdb72b99348c.ngrok.io/mutate-v1-namespace-owner-reference
|
||||
- caBundle:
|
||||
- service:
|
||||
- namespace: system
|
||||
- name: capsule
|
||||
- path: /mutate-v1-namespace-owner-reference
|
||||
...
|
||||
```
|
||||
|
||||
#### Run Capsule
|
||||
Finally, it's time to run locally Capsule using your preferred IDE (or not):
|
||||
from the project root path, you can issue the following command.
|
||||
|
||||
```
|
||||
make run
|
||||
```
|
||||
|
||||
All the logs will start to flow in your standard output, feel free to attach
|
||||
your debugger to set breakpoints as well!
|
||||
|
||||
## Code convention
|
||||
The changes must follow the Pull Request method where a _GitHub Action_ will
|
||||
check the `golangci-lint`, so ensure your changes respect the coding standard.
|
||||
|
||||
### golint
|
||||
You can easily check them issuing the _Make_ recipe `golint`.
|
||||
|
||||
```
|
||||
# make golint
|
||||
golangci-lint run -c .golangci.yml
|
||||
```
|
||||
|
||||
> Enabled linters and related options are defined in the [.golanci.yml file](../../.golangci.yml)
|
||||
|
||||
### goimports
|
||||
Also, the Go import statements must be sorted following the best practice:
|
||||
|
||||
```
|
||||
<STANDARD LIBRARY>
|
||||
|
||||
<EXTERNAL PACKAGES>
|
||||
|
||||
<LOCAL PACKAGES>
|
||||
```
|
||||
|
||||
To help you out you can use the _Make_ recipe `goimports`
|
||||
|
||||
```
|
||||
# make goimports
|
||||
goimports -w -l -local "github.com/clastix/capsule" .
|
||||
```
|
||||
|
||||
### Commits
|
||||
All the Pull Requests must refer to an already open issue: this is the first phase to contribute also for informing maintainers about the issue.
|
||||
|
||||
Commit's first line should not exceed 50 columns.
|
||||
|
||||
A commit description is welcomed to explain more the changes: just ensure
|
||||
to put a blank line and an arbitrary number of maximum 72 characters long
|
||||
lines, at most one blank line between them.
|
||||
|
||||
Please, split changes into several and documented small commits: this will help us to perform a better review. Commits must follow the Conventional Commits Specification, a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of. This convention dovetails with Semantic Versioning, by describing the features, fixes, and breaking changes made in commit messages. See [Conventional Commits Specification](https://www.conventionalcommits.org) to learn about Conventional Commits.
|
||||
|
||||
> In case of errors or need of changes to previous commits,
|
||||
> fix them squashing to make changes atomic.
|
||||
|
||||
### Miscellanea
|
||||
Please, add a new single line at end of any file as the current coding style.
|
||||
@@ -20,7 +20,7 @@ To simplify the usage of Capsule in this scenario, we'll work with the following
|
||||
|
||||
Use Capsule to address any of the following scenarios:
|
||||
|
||||
* [Onboard Tenants](./onboarding.md)
|
||||
* [Assign Tenant Ownership](./tenant-ownership.md)
|
||||
* [Create Namespaces](./create-namespaces.md)
|
||||
* [Assign Permissions](./permissions.md)
|
||||
* [Enforce Resources Quotas and Limits](./resources-quota-limits.md)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Onboard a new tenant
|
||||
# Tenant ownership
|
||||
Bill, the cluster admin, receives a new request from Acme Corp.'s CTO asking for a new tenant to be onboarded and Alice user will be the tenant owner. Bill then assigns Alice's identity of `alice` in the Acme Corp. identity management system. Since Alice is a tenant owner, Bill needs to assign `alice` the Capsule group defined by `--capsule-user-group` option, which defaults to `capsule.clastix.io`.
|
||||
|
||||
To keep things simple, we assume that Bill just creates a client certificate for authentication using X.509 Certificate Signing Request, so Alice's certificate has `"/CN=alice/O=capsule.clastix.io"`.
|
||||
@@ -136,5 +136,26 @@ kubectl --as system:serviceaccount:default:robot --as-group capsule.clastix.io a
|
||||
yes
|
||||
```
|
||||
|
||||
The service account has to be part of Capsule group, so Bill has to set in the `CapsuleConfiguration`
|
||||
|
||||
```yaml
|
||||
apiVersion: capsule.clastix.io/v1alpha1
|
||||
kind: CapsuleConfiguration
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
userGroups:
|
||||
- capsule.clastix.io
|
||||
- system:serviceaccounts:default
|
||||
```
|
||||
|
||||
because, by default, each service account is a member of following groups:
|
||||
|
||||
```
|
||||
system:serviceaccounts
|
||||
system:serviceaccounts:{service-account-namespace}
|
||||
system:authenticated
|
||||
```
|
||||
|
||||
# What’s next
|
||||
See how a tenant owner, creates new namespaces. [Create namespaces](./create-namespaces.md).
|
||||
@@ -6,7 +6,6 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
@@ -48,7 +47,6 @@ var _ = BeforeSuite(func(done Done) {
|
||||
|
||||
By("bootstrapping test environment")
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")},
|
||||
UseExistingCluster: func(v bool) *bool {
|
||||
return &v
|
||||
}(true),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This script uses Kubernetes CertificateSigningRequest (CSR) to generate a
|
||||
# certificate signed by the Kubernetes CA itself.
|
||||
@@ -10,23 +10,18 @@
|
||||
# Exit immediately if a command exits with a non-zero status.
|
||||
set -e
|
||||
|
||||
# Check if OpenSSL is installed
|
||||
if [[ ! -x "$(command -v openssl)" ]]; then
|
||||
echo "Error: openssl not found"
|
||||
exit 1
|
||||
fi
|
||||
function check_command() {
|
||||
local command=$1
|
||||
|
||||
# Check if kubectl is installed
|
||||
if [[ ! -x "$(command -v kubectl)" ]]; then
|
||||
echo "Error: kubectl not found"
|
||||
exit 1
|
||||
fi
|
||||
if ! command -v $command &> /dev/null; then
|
||||
echo "Error: ${command} not found"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Check if jq is installed
|
||||
if [[ ! -x "$(command -v jq)" ]]; then
|
||||
echo "Error: jq not found"
|
||||
exit 1
|
||||
fi
|
||||
check_command openssl
|
||||
check_command kubectl
|
||||
check_command jq
|
||||
|
||||
USER=$1
|
||||
TENANT=$2
|
||||
|
||||
Reference in New Issue
Block a user