mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-02-19 20:39:51 +00:00
Compare commits
304 Commits
v0.0.5
...
issues/451
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86f28a0202 | ||
|
|
fa8e805842 | ||
|
|
8df66fc232 | ||
|
|
c2218912eb | ||
|
|
e361e2d424 | ||
|
|
260b60d263 | ||
|
|
e0d5e6feb2 | ||
|
|
0784dc7177 | ||
|
|
b17c6c4636 | ||
|
|
52cf597041 | ||
|
|
b8dcded882 | ||
|
|
6a175e9017 | ||
|
|
3c609f84db | ||
|
|
7c3a59c4e4 | ||
|
|
d3e3b8a881 | ||
|
|
7a8148bd58 | ||
|
|
405d3ac52d | ||
|
|
f92acf9a9d | ||
|
|
bbb7b850d6 | ||
|
|
0f7284d190 | ||
|
|
7db263b2b6 | ||
|
|
0a8f50f761 | ||
|
|
7a66e8ea93 | ||
|
|
b5eb03ea76 | ||
|
|
681b514516 | ||
|
|
b28b98a7bc | ||
|
|
f6bf0ca446 | ||
|
|
1081bad7cb | ||
|
|
79372c7332 | ||
|
|
4e8faaf845 | ||
|
|
d1b008972c | ||
|
|
a14c7609df | ||
|
|
03456c0b54 | ||
|
|
ddfe2219a0 | ||
|
|
6b68363a46 | ||
|
|
357834c5b9 | ||
|
|
085d9f6503 | ||
|
|
196e3c910d | ||
|
|
0039c91c23 | ||
|
|
26965a5ea2 | ||
|
|
422b6598ba | ||
|
|
61e6ab4088 | ||
|
|
94c6a64fcb | ||
|
|
75ebb571e4 | ||
|
|
8f3b3eac29 | ||
|
|
7979c256d9 | ||
|
|
bdafbcf90a | ||
|
|
d0530bbbe3 | ||
|
|
1035afc7fe | ||
|
|
67046c5b54 | ||
|
|
564c4db81a | ||
|
|
30c3ab078d | ||
|
|
e9b803b9cd | ||
|
|
cb8e504832 | ||
|
|
713867d916 | ||
|
|
23e55c685c | ||
|
|
6393541818 | ||
|
|
c140ab076e | ||
|
|
6b629777b7 | ||
|
|
5554ed5f32 | ||
|
|
00ef9a2f67 | ||
|
|
46c2f0e997 | ||
|
|
0c0a90a934 | ||
|
|
9d65013a22 | ||
|
|
60ab33337d | ||
|
|
225d671301 | ||
|
|
7538926bae | ||
|
|
0de0eca72a | ||
|
|
d5a702ceae | ||
|
|
a2fda44110 | ||
|
|
06330cf992 | ||
|
|
1ec9936158 | ||
|
|
694b519af8 | ||
|
|
0b34f04291 | ||
|
|
a702ef2af2 | ||
|
|
04d91af9f5 | ||
|
|
8949be7497 | ||
|
|
df08c9e63e | ||
|
|
07daffd669 | ||
|
|
3a42b90221 | ||
|
|
09277e9f3d | ||
|
|
47794c0cf8 | ||
|
|
e24394f329 | ||
|
|
01053d5deb | ||
|
|
b749e34547 | ||
|
|
82480f3afd | ||
|
|
88a9c242a4 | ||
|
|
651c62ff4a | ||
|
|
dcb8b784d5 | ||
|
|
7a698633d7 | ||
|
|
894ea5016b | ||
|
|
e4e3283b90 | ||
|
|
007f0083c2 | ||
|
|
bc6fc920d3 | ||
|
|
01b511b509 | ||
|
|
6223b1c297 | ||
|
|
d5158f06be | ||
|
|
047f4a0ff7 | ||
|
|
71cdb45925 | ||
|
|
9182895811 | ||
|
|
2eceb0935a | ||
|
|
8ead555743 | ||
|
|
57bf3d1c1b | ||
|
|
bb58e90f5d | ||
|
|
f8fa87a998 | ||
|
|
b3658b7bfc | ||
|
|
54d0201161 | ||
|
|
44ffe0ddf5 | ||
|
|
491ab71842 | ||
|
|
4e9dbf8690 | ||
|
|
34614015a0 | ||
|
|
737fb26e39 | ||
|
|
b56015922f | ||
|
|
ddb9ffd79e | ||
|
|
cae65c9f84 | ||
|
|
befcf65bdd | ||
|
|
e1d98334a2 | ||
|
|
848c6d99c2 | ||
|
|
bd12068397 | ||
|
|
4604e44c37 | ||
|
|
31863b53af | ||
|
|
7a055fcb9f | ||
|
|
29ab5ca64a | ||
|
|
c52f7844db | ||
|
|
9244122d42 | ||
|
|
f883e7b662 | ||
|
|
2f5f31b678 | ||
|
|
e7ef9642ad | ||
|
|
34f73af5c4 | ||
|
|
18912a002b | ||
|
|
d43ad2f9f8 | ||
|
|
9a595877ce | ||
|
|
c0d4aab582 | ||
|
|
6761fb93dc | ||
|
|
bf9e0f6b10 | ||
|
|
f937942c49 | ||
|
|
89d7f301c6 | ||
|
|
2a6ff09340 | ||
|
|
35f48107fc | ||
|
|
7aa62b6f1d | ||
|
|
58645f39bb | ||
|
|
0e55823a0c | ||
|
|
ba690480a7 | ||
|
|
faa2306a30 | ||
|
|
c1448c82e9 | ||
|
|
776a56b5bc | ||
|
|
e4883bb737 | ||
|
|
e70afb5e77 | ||
|
|
ee7af18f98 | ||
|
|
ac7de3bf88 | ||
|
|
8883b15aa9 | ||
|
|
e23132c820 | ||
|
|
bec59a585e | ||
|
|
9c649ac7eb | ||
|
|
3455aed503 | ||
|
|
ad1edf57ac | ||
|
|
d64dcb5a44 | ||
|
|
76d7697703 | ||
|
|
96f4f31c17 | ||
|
|
c3f9dfe652 | ||
|
|
502e9a556f | ||
|
|
6f208a6e0e | ||
|
|
1fb52003d5 | ||
|
|
98e1640d9b | ||
|
|
eb19a7a89f | ||
|
|
db8b8ac1d9 | ||
|
|
663ce93a3e | ||
|
|
a6408f26b0 | ||
|
|
1aa026c977 | ||
|
|
6008373960 | ||
|
|
414c03a874 | ||
|
|
4d34a9e3d7 | ||
|
|
cb9b560926 | ||
|
|
ef75d0496a | ||
|
|
e1e75a093b | ||
|
|
80143ffd50 | ||
|
|
3d54810f19 | ||
|
|
09dfe33a6a | ||
|
|
01ea36b462 | ||
|
|
bd448d8c29 | ||
|
|
b58ca3a7d7 | ||
|
|
52fb0948cb | ||
|
|
1b0fa587eb | ||
|
|
92655f1872 | ||
|
|
44bf846260 | ||
|
|
e6b433dcd7 | ||
|
|
3e0882dbc8 | ||
|
|
416609362d | ||
|
|
3d714dc124 | ||
|
|
bd01881dd3 | ||
|
|
ac6af13b07 | ||
|
|
8fb4b7d4a1 | ||
|
|
d4280b8d7e | ||
|
|
6e39b17e7c | ||
|
|
b1a9603faa | ||
|
|
0d4201a6c2 | ||
|
|
1734c906a9 | ||
|
|
184f054f2f | ||
|
|
126449b796 | ||
|
|
284e7da66f | ||
|
|
99e1589828 | ||
|
|
7cc2c3f4e9 | ||
|
|
ba07f99c6e | ||
|
|
d79972691e | ||
|
|
25f021e151 | ||
|
|
320353e561 | ||
|
|
5bd2af89e4 | ||
|
|
61d394f844 | ||
|
|
fc8f5a08fe | ||
|
|
ffcb7c4408 | ||
|
|
8d1a109f1c | ||
|
|
a19045419d | ||
|
|
7574335a8a | ||
|
|
72e97b9960 | ||
|
|
b3c6082a1e | ||
|
|
9a940096c9 | ||
|
|
f9becf39e5 | ||
|
|
e1160b8862 | ||
|
|
6472b221ed | ||
|
|
a2e5bbf26d | ||
|
|
8804496bb2 | ||
|
|
5de0a6d712 | ||
|
|
531cc4cf14 | ||
|
|
3e33290c4c | ||
|
|
824442b9ee | ||
|
|
34583352e5 | ||
|
|
5681228789 | ||
|
|
7237972b80 | ||
|
|
46fc65a988 | ||
|
|
44acfaed86 | ||
|
|
7ca087cac5 | ||
|
|
b2b640dc96 | ||
|
|
5b35e0b0d5 | ||
|
|
accd9ca038 | ||
|
|
e7b33bda26 | ||
|
|
08fbd26ec8 | ||
|
|
006b0c80cf | ||
|
|
b6f3fccbea | ||
|
|
bf79c25a8a | ||
|
|
630e802708 | ||
|
|
e5a1861cac | ||
|
|
246c1a3c2c | ||
|
|
a06e68945c | ||
|
|
61c9bc647c | ||
|
|
9c8b0377dc | ||
|
|
dfe0f5ea49 | ||
|
|
a1a2e5e00c | ||
|
|
20aa7657e4 | ||
|
|
7c1592e739 | ||
|
|
f60f2b1542 | ||
|
|
53377e994c | ||
|
|
d0893a5aa9 | ||
|
|
a7fff597fa | ||
|
|
a4128b5744 | ||
|
|
b349042265 | ||
|
|
40bdf0cd25 | ||
|
|
20d0ef8ed0 | ||
|
|
61034947fd | ||
|
|
ca7b85971b | ||
|
|
73e6a17527 | ||
|
|
9103a14506 | ||
|
|
d532f1633c | ||
|
|
3570b02427 | ||
|
|
994a4c282d | ||
|
|
eff1282e34 | ||
|
|
52a73e011c | ||
|
|
4ccef411ab | ||
|
|
dfb0a536b7 | ||
|
|
9ef64d0f8c | ||
|
|
5649283058 | ||
|
|
0481822555 | ||
|
|
bcbd9c2781 | ||
|
|
229b569b50 | ||
|
|
ef6eea62dc | ||
|
|
bb6614d1e8 | ||
|
|
784f3a71df | ||
|
|
3c9895e498 | ||
|
|
6dc83b16da | ||
|
|
e6da507d10 | ||
|
|
5bca3b7da7 | ||
|
|
2e188d26f9 | ||
|
|
3afee659ff | ||
|
|
c22cb6cc88 | ||
|
|
202a18c132 | ||
|
|
8441d8878a | ||
|
|
d5af190c51 | ||
|
|
82ae78b704 | ||
|
|
6c44a6a4d3 | ||
|
|
d6e7437b6c | ||
|
|
ac7114e975 | ||
|
|
2fdc08c2f4 | ||
|
|
c2cede6287 | ||
|
|
36c90d485e | ||
|
|
34c958371b | ||
|
|
e5f17d1e0d | ||
|
|
e1b203727d | ||
|
|
cec8cc0573 | ||
|
|
7ca9fe0c63 | ||
|
|
b87a6c022f | ||
|
|
01b75a5094 | ||
|
|
2c6dcf0dd7 | ||
|
|
7994ae1da1 | ||
|
|
12237ae106 | ||
|
|
d8449fee24 |
12
.github/FUNDING.yml
vendored
12
.github/FUNDING.yml
vendored
@@ -1,12 +0,0 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [prometherion]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
33
.github/workflows/ci.yml
vendored
33
.github/workflows/ci.yml
vendored
@@ -7,6 +7,15 @@ on:
|
||||
branches: [ "*" ]
|
||||
|
||||
jobs:
|
||||
commit_lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: wagoid/commitlint-github-action@v2
|
||||
with:
|
||||
firstParent: true
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
@@ -23,23 +32,15 @@ jobs:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Cache Go modules
|
||||
uses: actions/cache@v1
|
||||
env:
|
||||
cache-name: go-mod
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
/home/runner/work/capsule/capsule
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-
|
||||
${{ runner.os }}-
|
||||
- run: make manifests
|
||||
- name: Checking if manifests are disaligned
|
||||
run: test -z "$(git diff 2> /dev/null)"
|
||||
- name: Checking if manifests generated untracked files
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.16'
|
||||
- run: make installer
|
||||
- name: Checking if YAML installer file is not aligned
|
||||
run: if [[ $(git diff | wc -l) -gt 0 ]]; then echo ">>> Untracked generated files have not been committed" && git --no-pager diff && exit 1; fi
|
||||
- name: Checking if YAML installer generated untracked files
|
||||
run: test -z "$(git ls-files --others --exclude-standard 2> /dev/null)"
|
||||
- run: make fmt vet
|
||||
- name: Checking if source code is not formatted
|
||||
run: test -z "$(git diff 2> /dev/null)"
|
||||
|
||||
89
.github/workflows/docker-ci.yml
vendored
Normal file
89
.github/workflows/docker-ci.yml
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
name: docker-ci
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
docker-ci:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Generate build-args
|
||||
id: build-args
|
||||
run: |
|
||||
# Declare vars for internal use
|
||||
VERSION=$(git describe --abbrev=0 --tags)
|
||||
GIT_HEAD_COMMIT=$(git rev-parse --short HEAD)
|
||||
GIT_TAG_COMMIT=$(git rev-parse --short $VERSION)
|
||||
GIT_MODIFIED_1=$(git diff $GIT_HEAD_COMMIT $GIT_TAG_COMMIT --quiet && echo "" || echo ".dev")
|
||||
GIT_MODIFIED_2=$(git diff --quiet && echo "" || echo ".dirty")
|
||||
# Export to GH_ENV
|
||||
echo "GIT_LAST_TAG=$VERSION" >> $GITHUB_ENV
|
||||
echo "GIT_HEAD_COMMIT=$GIT_HEAD_COMMIT" >> $GITHUB_ENV
|
||||
echo "GIT_TAG_COMMIT=$GIT_TAG_COMMIT" >> $GITHUB_ENV
|
||||
echo "GIT_MODIFIED=$(echo "$GIT_MODIFIED_1""$GIT_MODIFIED_2")" >> $GITHUB_ENV
|
||||
echo "GIT_REPO=$(git config --get remote.origin.url)" >> $GITHUB_ENV
|
||||
echo "BUILD_DATE=$(git log -1 --format="%at" | xargs -I{} date -d @{} +%Y-%m-%dT%H:%M:%S)" >> $GITHUB_ENV
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
with:
|
||||
images: |
|
||||
quay.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=semver,pattern={{raw}}
|
||||
flavor: |
|
||||
latest=false
|
||||
|
||||
- name: Set up QEMU
|
||||
id: qemu
|
||||
uses: docker/setup-qemu-action@v1
|
||||
with:
|
||||
platforms: arm64,arm
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
with:
|
||||
install: true
|
||||
|
||||
- name: Inspect builder
|
||||
run: |
|
||||
echo "Name: ${{ steps.buildx.outputs.name }}"
|
||||
echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
|
||||
echo "Status: ${{ steps.buildx.outputs.status }}"
|
||||
echo "Flags: ${{ steps.buildx.outputs.flags }}"
|
||||
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
|
||||
|
||||
- name: Login to quay.io Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: quay.io
|
||||
username: ${{ github.repository_owner }}+github
|
||||
password: ${{ secrets.BOT_QUAY_IO }}
|
||||
|
||||
- name: Build and push
|
||||
id: build-release
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
file: Dockerfile
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64,linux/arm
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
build-args: |
|
||||
GIT_HEAD_COMMIT=${{ env.GIT_HEAD_COMMIT }}
|
||||
GIT_TAG_COMMIT=${{ env.GIT_TAG_COMMIT }}
|
||||
GIT_REPO=${{ env.GIT_REPO }}
|
||||
GIT_LAST_TAG=${{ env.GIT_LAST_TAG }}
|
||||
GIT_MODIFIED=${{ env.GIT_MODIFIED }}
|
||||
BUILD_DATE=${{ env.BUILD_DATE }}
|
||||
|
||||
- name: Image digest
|
||||
run: echo ${{ steps.build-release.outputs.digest }}
|
||||
37
.github/workflows/e2e.yml
vendored
37
.github/workflows/e2e.yml
vendored
@@ -3,33 +3,41 @@ name: e2e
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
paths:
|
||||
- '.github/workflows/e2e.yml'
|
||||
- 'api/**'
|
||||
- 'controllers/**'
|
||||
- 'e2e/*'
|
||||
- 'Dockerfile'
|
||||
- 'go.*'
|
||||
- 'main.go'
|
||||
- 'Makefile'
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
paths:
|
||||
- '.github/workflows/e2e.yml'
|
||||
- 'api/**'
|
||||
- 'controllers/**'
|
||||
- 'e2e/*'
|
||||
- 'Dockerfile'
|
||||
- 'go.*'
|
||||
- 'main.go'
|
||||
- 'Makefile'
|
||||
|
||||
jobs:
|
||||
kind:
|
||||
name: Kubernetes
|
||||
strategy:
|
||||
matrix:
|
||||
k8s-version: ['v1.16.15', 'v1.17.11', 'v1.18.8', 'v1.19.4', 'v1.20.0']
|
||||
k8s-version: ['v1.16.15', 'v1.17.11', 'v1.18.8', 'v1.19.4', 'v1.20.7', 'v1.21.2', 'v1.22.0']
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Cache Go modules and Docker images
|
||||
uses: actions/cache@v1
|
||||
env:
|
||||
cache-name: gomod-docker
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
/var/lib/docker
|
||||
/home/runner/work/capsule/capsule
|
||||
key: ${{ matrix.k8s-version }}-build-${{ env.cache-name }}
|
||||
restore-keys: |
|
||||
${{ matrix.k8s-version }}-build-
|
||||
${{ matrix.k8s-version }}-
|
||||
go-version: '^1.16'
|
||||
- run: make manifests
|
||||
- name: Checking if manifests are disaligned
|
||||
run: test -z "$(git diff 2> /dev/null)"
|
||||
@@ -39,10 +47,11 @@ jobs:
|
||||
run: go get github.com/onsi/ginkgo/ginkgo
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '^1.13.8'
|
||||
go-version: '^1.16'
|
||||
- uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
skipClusterCreation: true
|
||||
version: v0.11.1
|
||||
- uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: 3.3.4
|
||||
|
||||
6
.github/workflows/helm.yml
vendored
6
.github/workflows/helm.yml
vendored
@@ -3,8 +3,12 @@ name: Helm Chart
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
tags: [ "helm-v*" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
create:
|
||||
branches: [ "*" ]
|
||||
tags: [ "helm-v*" ]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
@@ -17,7 +21,7 @@ jobs:
|
||||
- name: Linting Chart
|
||||
run: helm lint ./charts/capsule
|
||||
release:
|
||||
if: github.ref == 'refs/heads/master'
|
||||
if: startsWith(github.ref, 'refs/tags/helm-v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,6 +22,7 @@ bin
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.vscode
|
||||
|
||||
**/*.kubeconfig
|
||||
**/*.crt
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Build the manager binary
|
||||
FROM golang:1.13 as builder
|
||||
FROM golang:1.16 as builder
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG GIT_HEAD_COMMIT
|
||||
ARG GIT_TAG_COMMIT
|
||||
ARG GIT_LAST_TAG
|
||||
@@ -24,7 +25,7 @@ COPY controllers/ controllers/
|
||||
COPY pkg/ pkg/
|
||||
|
||||
# Build
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build \
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH GO111MODULE=on go build \
|
||||
-gcflags "-N -l" \
|
||||
-ldflags "-X main.GitRepo=$GIT_REPO -X main.GitTag=$GIT_LAST_TAG -X main.GitCommit=$GIT_HEAD_COMMIT -X main.GitDirty=$GIT_MODIFIED -X main.BuildTime=$BUILD_DATE" \
|
||||
-o manager
|
||||
|
||||
144
Makefile
144
Makefile
@@ -1,5 +1,5 @@
|
||||
# Current Operator version
|
||||
VERSION ?= $$(git describe --abbrev=0 --tags)
|
||||
VERSION ?= $$(git describe --abbrev=0 --tags --match "v*")
|
||||
|
||||
# Default bundle image tag
|
||||
BUNDLE_IMG ?= quay.io/clastix/capsule:$(VERSION)-bundle
|
||||
@@ -15,7 +15,7 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
|
||||
# Image URL to use all building/pushing image targets
|
||||
IMG ?= quay.io/clastix/capsule:$(VERSION)
|
||||
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
|
||||
CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
|
||||
CRD_OPTIONS ?= "crd:preserveUnknownFields=false"
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
@@ -31,12 +31,12 @@ GIT_MODIFIED_1 ?= $$(git diff $(GIT_HEAD_COMMIT) $(GIT_TAG_COMMIT) --quiet && e
|
||||
GIT_MODIFIED_2 ?= $$(git diff --quiet && echo "" || echo ".dirty")
|
||||
GIT_MODIFIED ?= $$(echo "$(GIT_MODIFIED_1)$(GIT_MODIFIED_2)")
|
||||
GIT_REPO ?= $$(git config --get remote.origin.url)
|
||||
BUILD_DATE ?= $$(date '+%Y-%m-%dT%H:%M:%S')
|
||||
BUILD_DATE ?= $$(git log -1 --format="%at" | xargs -I{} date -d @{} +%Y-%m-%dT%H:%M:%S)
|
||||
|
||||
all: manager
|
||||
|
||||
# Run tests
|
||||
test: generate fmt vet manifests
|
||||
test: generate manifests
|
||||
go test ./... -coverprofile cover.out
|
||||
|
||||
# Build manager binary
|
||||
@@ -44,44 +44,91 @@ manager: generate fmt vet
|
||||
go build -o bin/manager main.go
|
||||
|
||||
# Run against the configured Kubernetes cluster in ~/.kube/config
|
||||
run: generate fmt vet manifests
|
||||
go run ./main.go
|
||||
run: generate manifests
|
||||
go run .
|
||||
|
||||
# Creates the single file to install Capsule without any external dependency
|
||||
installer: manifests kustomize
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
$(KUSTOMIZE) build config/default > config/install.yaml
|
||||
|
||||
# Install CRDs into a cluster
|
||||
install: manifests kustomize
|
||||
install: installer
|
||||
$(KUSTOMIZE) build config/crd | kubectl apply -f -
|
||||
|
||||
# Uninstall CRDs from a cluster
|
||||
uninstall: manifests kustomize
|
||||
uninstall: installer
|
||||
$(KUSTOMIZE) build config/crd | kubectl delete -f -
|
||||
|
||||
# Deploy controller in the configured Kubernetes cluster in ~/.kube/config
|
||||
deploy: manifests kustomize
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
$(KUSTOMIZE) build config/default | kubectl apply -f -
|
||||
deploy: installer
|
||||
kubectl apply -f config/install.yaml
|
||||
|
||||
# Remove controller in the configured Kubernetes cluster in ~/.kube/config
|
||||
remove: manifests kustomize
|
||||
$(KUSTOMIZE) build config/default | kubectl delete -f -
|
||||
remove: installer
|
||||
kubectl delete -f config/install.yaml
|
||||
kubectl delete clusterroles.rbac.authorization.k8s.io capsule-namespace-deleter capsule-namespace-provisioner --ignore-not-found
|
||||
kubectl delete clusterrolebindings.rbac.authorization.k8s.io capsule-namespace-provisioner --ignore-not-found
|
||||
kubectl delete clusterrolebindings.rbac.authorization.k8s.io capsule-namespace-deleter capsule-namespace-provisioner --ignore-not-found
|
||||
|
||||
# Generate manifests e.g. CRD, RBAC etc.
|
||||
manifests: controller-gen
|
||||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
|
||||
# Run go fmt against code
|
||||
fmt:
|
||||
go fmt ./...
|
||||
|
||||
# Run go vet against code
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
# Generate code
|
||||
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) \
|
||||
@@ -95,37 +142,27 @@ docker-build: test
|
||||
docker-push:
|
||||
docker push ${IMG}
|
||||
|
||||
# find or download controller-gen
|
||||
# download controller-gen if necessary
|
||||
controller-gen:
|
||||
ifeq (, $(shell which controller-gen))
|
||||
@{ \
|
||||
set -e ;\
|
||||
CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\
|
||||
cd $$CONTROLLER_GEN_TMP_DIR ;\
|
||||
go mod init tmp ;\
|
||||
go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.3.0 ;\
|
||||
rm -rf $$CONTROLLER_GEN_TMP_DIR ;\
|
||||
}
|
||||
CONTROLLER_GEN=$(GOBIN)/controller-gen
|
||||
else
|
||||
CONTROLLER_GEN=$(shell which controller-gen)
|
||||
endif
|
||||
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
|
||||
controller-gen: ## Download controller-gen locally if necessary.
|
||||
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.5.0)
|
||||
|
||||
kustomize:
|
||||
ifeq (, $(shell which kustomize))
|
||||
@{ \
|
||||
set -e ;\
|
||||
KUSTOMIZE_GEN_TMP_DIR=$$(mktemp -d) ;\
|
||||
cd $$KUSTOMIZE_GEN_TMP_DIR ;\
|
||||
go mod init tmp ;\
|
||||
go get sigs.k8s.io/kustomize/kustomize/v3@v3.5.4 ;\
|
||||
rm -rf $$KUSTOMIZE_GEN_TMP_DIR ;\
|
||||
}
|
||||
KUSTOMIZE=$(GOBIN)/kustomize
|
||||
else
|
||||
KUSTOMIZE=$(shell which kustomize)
|
||||
endif
|
||||
KUSTOMIZE = $(shell pwd)/bin/kustomize
|
||||
kustomize: ## Download kustomize locally if necessary.
|
||||
$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7)
|
||||
|
||||
# go-get-tool will 'go get' any package $2 and install it to $1.
|
||||
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
define go-get-tool
|
||||
@[ -f $(1) ] || { \
|
||||
set -e ;\
|
||||
TMP_DIR=$$(mktemp -d) ;\
|
||||
cd $$TMP_DIR ;\
|
||||
go mod init tmp ;\
|
||||
echo "Downloading $(2)" ;\
|
||||
GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
|
||||
rm -rf $$TMP_DIR ;\
|
||||
}
|
||||
endef
|
||||
|
||||
# Generate bundle manifests and metadata, then validate generated files.
|
||||
bundle: manifests
|
||||
@@ -157,12 +194,13 @@ e2e/%:
|
||||
--debug \
|
||||
--install \
|
||||
--namespace capsule-system \
|
||||
--create-namespace capsule \
|
||||
--create-namespace \
|
||||
--set 'manager.image.pullPolicy=Never' \
|
||||
--set 'manager.resources=null'\
|
||||
--set "manager.image.tag=$(VERSION)" \
|
||||
--set 'manager.livenessProbe.failureThreshold=10' \
|
||||
--set 'manager.readinessProbe.failureThreshold=10' \
|
||||
capsule \
|
||||
./charts/capsule
|
||||
ginkgo -v -tags e2e ./e2e
|
||||
kind delete cluster --name capsule
|
||||
|
||||
41
PROJECT
41
PROJECT
@@ -1,10 +1,39 @@
|
||||
domain: github.com/clastix/capsule
|
||||
layout: go.kubebuilder.io/v2
|
||||
domain: clastix.io
|
||||
layout:
|
||||
- go.kubebuilder.io/v3
|
||||
plugins:
|
||||
manifests.sdk.operatorframework.io/v2: {}
|
||||
scorecard.sdk.operatorframework.io/v2: {}
|
||||
projectName: capsule
|
||||
repo: github.com/clastix/capsule
|
||||
resources:
|
||||
- group: capsule.clastix.io
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: false
|
||||
controller: true
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: Tenant
|
||||
path: github.com/clastix/capsule/api/v1alpha1
|
||||
version: v1alpha1
|
||||
version: 3-alpha
|
||||
plugins:
|
||||
go.operator-sdk.io/v2-alpha: {}
|
||||
webhooks:
|
||||
conversion: true
|
||||
webhookVersion: v1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: false
|
||||
controller: true
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: CapsuleConfiguration
|
||||
path: github.com/clastix/capsule/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: false
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: Tenant
|
||||
path: github.com/clastix/capsule/api/v1beta1
|
||||
version: v1beta1
|
||||
version: "3"
|
||||
|
||||
84
README.md
84
README.md
@@ -13,7 +13,7 @@
|
||||
|
||||
---
|
||||
|
||||
# Kubernetes multi-tenancy made simple
|
||||
# Kubernetes multi-tenancy made easy
|
||||
**Capsule** helps to implement a multi-tenancy and policy-based environment in your Kubernetes cluster. It is not intended to be yet another _PaaS_, instead, it has been designed as a micro-services-based ecosystem with the minimalist approach, leveraging only on upstream Kubernetes.
|
||||
|
||||
# What's the problem with the current status?
|
||||
@@ -61,35 +61,34 @@ Make sure you have access to a Kubernetes cluster as administrator.
|
||||
There are two ways to install Capsule:
|
||||
|
||||
* Use the Helm Chart available [here](./charts/capsule/README.md)
|
||||
* Use [`kustomize`](https://github.com/kubernetes-sigs/kustomize)
|
||||
* Use the [single YAML file installer](./config/install.yaml)
|
||||
|
||||
## Install with kustomize
|
||||
Ensure you have `kubectl` and `kustomize` installed in your `PATH`.
|
||||
## Install with the single YAML file installer
|
||||
|
||||
Ensure you have `kubectl` installed in your `PATH`.
|
||||
|
||||
Clone this repository and move to the repo folder:
|
||||
|
||||
```
|
||||
$ git clone https://github.com/clastix/capsule
|
||||
$ cd capsule
|
||||
$ make deploy
|
||||
$ kubectl apply -f https://raw.githubusercontent.com/clastix/capsule/master/config/install.yaml
|
||||
```
|
||||
|
||||
It will install the Capsule controller in a dedicated namespace `capsule-system`.
|
||||
|
||||
## How to create Tenants
|
||||
Use the scaffold [Tenant](config/samples/capsule_v1alpha1_tenant.yaml) and simply apply as cluster admin.
|
||||
Use the scaffold [Tenant](config/samples/capsule_v1beta1_tenant.yaml) and simply apply as cluster admin.
|
||||
|
||||
```
|
||||
$ kubectl apply -f config/samples/capsule_v1alpha1_tenant.yaml
|
||||
tenant.capsule.clastix.io/oil created
|
||||
$ kubectl apply -f config/samples/capsule_v1beta1_tenant.yaml
|
||||
tenant.capsule.clastix.io/gas created
|
||||
```
|
||||
|
||||
You can check the tenant just created as
|
||||
|
||||
```
|
||||
$ kubectl get tenants
|
||||
NAME NAMESPACE QUOTA NAMESPACE COUNT OWNER NAME OWNER KIND NODE SELECTOR AGE
|
||||
oil 3 0 alice User 1m
|
||||
NAME STATE NAMESPACE QUOTA NAMESPACE COUNT NODE SELECTOR AGE
|
||||
gas Active 3 0 {"kubernetes.io/os":"linux"} 25s
|
||||
```
|
||||
|
||||
## Tenant owners
|
||||
@@ -101,52 +100,46 @@ Assignment to a group depends on the authentication strategy in your cluster.
|
||||
|
||||
For example, if you are using `capsule.clastix.io`, users authenticated through a _X.509_ certificate must have `capsule.clastix.io` as _Organization_: `-subj "/CN=${USER}/O=capsule.clastix.io"`
|
||||
|
||||
Users authenticated through an _OIDC token_ must have
|
||||
Users authenticated through an _OIDC token_ must have in their token:
|
||||
|
||||
```json
|
||||
...
|
||||
"users_groups": [
|
||||
"capsule.clastix.io",
|
||||
"other_group"
|
||||
"capsule.clastix.io",
|
||||
"other_group"
|
||||
]
|
||||
```
|
||||
|
||||
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](hack/create-user.sh) can help you set up a dummy `kubeconfig` for the `bob` user acting as owner of a tenant called `gas`
|
||||
|
||||
```bash
|
||||
./hack/create-user.sh alice oil
|
||||
creating certs in TMPDIR /tmp/tmp.4CLgpuime3
|
||||
Generating RSA private key, 2048 bit long modulus (2 primes)
|
||||
............+++++
|
||||
........................+++++
|
||||
e is 65537 (0x010001)
|
||||
certificatesigningrequest.certificates.k8s.io/alice-oil created
|
||||
certificatesigningrequest.certificates.k8s.io/alice-oil approved
|
||||
kubeconfig file is: alice-oil.kubeconfig
|
||||
to use it as alice export KUBECONFIG=alice-oil.kubeconfig
|
||||
./hack/create-user.sh bob gas
|
||||
...
|
||||
certificatesigningrequest.certificates.k8s.io/bob-gas created
|
||||
certificatesigningrequest.certificates.k8s.io/bob-gas approved
|
||||
kubeconfig file is: bob-gas.kubeconfig
|
||||
to use it as bob export KUBECONFIG=bob-gas.kubeconfig
|
||||
```
|
||||
|
||||
## Working with Tenants
|
||||
Log in to the Kubernetes cluster as `alice` tenant owner
|
||||
Log in to the Kubernetes cluster as `bob` tenant owner
|
||||
|
||||
```
|
||||
$ export KUBECONFIG=alice-oil.kubeconfig
|
||||
$ export KUBECONFIG=bob-gas.kubeconfig
|
||||
```
|
||||
|
||||
and create a couple of new namespaces
|
||||
|
||||
```
|
||||
$ kubectl create namespace oil-production
|
||||
$ kubectl create namespace oil-development
|
||||
$ kubectl create namespace gas-production
|
||||
$ kubectl create namespace gas-development
|
||||
```
|
||||
|
||||
As user `alice` you can operate with fully admin permissions:
|
||||
As user `bob` you can operate with fully admin permissions:
|
||||
|
||||
```
|
||||
$ kubectl -n oil-development run nginx --image=docker.io/nginx
|
||||
$ kubectl -n oil-development get pods
|
||||
$ kubectl -n gas-development run nginx --image=docker.io/nginx
|
||||
$ kubectl -n gas-development get pods
|
||||
```
|
||||
|
||||
but limited to only your own namespaces:
|
||||
@@ -154,12 +147,9 @@ but limited to only your own namespaces:
|
||||
```
|
||||
$ kubectl -n kube-system get pods
|
||||
Error from server (Forbidden): pods is forbidden:
|
||||
User "alice" cannot list resource "pods" in API group "" in the namespace "kube-system"
|
||||
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.
|
||||
|
||||
@@ -167,20 +157,26 @@ 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.
|
||||
|
||||
- Q. Does it work with my Kuberentes XYZ distribution?
|
||||
- Q. Does it work with my Kubernetes XYZ distribution?
|
||||
|
||||
A. We tested Capsule with vanilla Kubernetes 1.16+ on private environments and public clouds. We expect it to work smoothly on any other Kubernetes distribution. Please, let us know if you find it doesn't.
|
||||
|
||||
|
||||
9
api/v1alpha1/additional_metadata.go
Normal file
9
api/v1alpha1/additional_metadata.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
type AdditionalMetadataSpec struct {
|
||||
AdditionalLabels map[string]string `json:"additionalLabels,omitempty"`
|
||||
AdditionalAnnotations map[string]string `json:"additionalAnnotations,omitempty"`
|
||||
}
|
||||
12
api/v1alpha1/additional_role_bindings.go
Normal file
12
api/v1alpha1/additional_role_bindings.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import rbacv1 "k8s.io/api/rbac/v1"
|
||||
|
||||
type AdditionalRoleBindingsSpec struct {
|
||||
ClusterRoleName string `json:"clusterRoleName"`
|
||||
// kubebuilder:validation:Minimum=1
|
||||
Subjects []rbacv1.Subject `json:"subjects"`
|
||||
}
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
|
||||
45
api/v1alpha1/capsuleconfiguration_types.go
Normal file
45
api/v1alpha1/capsuleconfiguration_types.go
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// CapsuleConfigurationSpec defines the Capsule configuration
|
||||
type CapsuleConfigurationSpec struct {
|
||||
// Names of the groups for Capsule users.
|
||||
// +kubebuilder:default={capsule.clastix.io}
|
||||
UserGroups []string `json:"userGroups,omitempty"`
|
||||
// Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix,
|
||||
// separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
|
||||
// +kubebuilder:default=false
|
||||
ForceTenantPrefix bool `json:"forceTenantPrefix,omitempty"`
|
||||
// Disallow creation of namespaces, whose name matches this regexp
|
||||
ProtectedNamespaceRegexpString string `json:"protectedNamespaceRegex,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
|
||||
// CapsuleConfiguration is the Schema for the Capsule configuration API
|
||||
type CapsuleConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec CapsuleConfigurationSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// CapsuleConfigurationList contains a list of CapsuleConfiguration
|
||||
type CapsuleConfigurationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []CapsuleConfiguration `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&CapsuleConfiguration{}, &CapsuleConfigurationList{})
|
||||
}
|
||||
560
api/v1alpha1/conversion_hub.go
Normal file
560
api/v1alpha1/conversion_hub.go
Normal file
@@ -0,0 +1,560 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/conversion"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
resourceQuotaScopeAnnotation = "capsule.clastix.io/resource-quota-scope"
|
||||
|
||||
podAllowedImagePullPolicyAnnotation = "capsule.clastix.io/allowed-image-pull-policy"
|
||||
|
||||
podPriorityAllowedAnnotation = "priorityclass.capsule.clastix.io/allowed"
|
||||
podPriorityAllowedRegexAnnotation = "priorityclass.capsule.clastix.io/allowed-regex"
|
||||
|
||||
enableNodePortsAnnotation = "capsule.clastix.io/enable-node-ports"
|
||||
enableExternalNameAnnotation = "capsule.clastix.io/enable-external-name"
|
||||
enableLoadBalancerAnnotation = "capsule.clastix.io/enable-loadbalancer-service"
|
||||
|
||||
ownerGroupsAnnotation = "owners.capsule.clastix.io/group"
|
||||
ownerUsersAnnotation = "owners.capsule.clastix.io/user"
|
||||
ownerServiceAccountAnnotation = "owners.capsule.clastix.io/serviceaccount"
|
||||
|
||||
enableNodeListingAnnotation = "capsule.clastix.io/enable-node-listing"
|
||||
enableNodeUpdateAnnotation = "capsule.clastix.io/enable-node-update"
|
||||
enableNodeDeletionAnnotation = "capsule.clastix.io/enable-node-deletion"
|
||||
enableStorageClassListingAnnotation = "capsule.clastix.io/enable-storageclass-listing"
|
||||
enableStorageClassUpdateAnnotation = "capsule.clastix.io/enable-storageclass-update"
|
||||
enableStorageClassDeletionAnnotation = "capsule.clastix.io/enable-storageclass-deletion"
|
||||
enableIngressClassListingAnnotation = "capsule.clastix.io/enable-ingressclass-listing"
|
||||
enableIngressClassUpdateAnnotation = "capsule.clastix.io/enable-ingressclass-update"
|
||||
enableIngressClassDeletionAnnotation = "capsule.clastix.io/enable-ingressclass-deletion"
|
||||
enablePriorityClassListingAnnotation = "capsule.clastix.io/enable-priorityclass-listing"
|
||||
enablePriorityClassUpdateAnnotation = "capsule.clastix.io/enable-priorityclass-update"
|
||||
enablePriorityClassDeletionAnnotation = "capsule.clastix.io/enable-priorityclass-deletion"
|
||||
|
||||
ingressHostnameCollisionScope = "ingress.capsule.clastix.io/hostname-collision-scope"
|
||||
)
|
||||
|
||||
func (t *Tenant) convertV1Alpha1OwnerToV1Beta1() capsulev1beta1.OwnerListSpec {
|
||||
var serviceKindToAnnotationMap = map[capsulev1beta1.ProxyServiceKind][]string{
|
||||
capsulev1beta1.NodesProxy: {enableNodeListingAnnotation, enableNodeUpdateAnnotation, enableNodeDeletionAnnotation},
|
||||
capsulev1beta1.StorageClassesProxy: {enableStorageClassListingAnnotation, enableStorageClassUpdateAnnotation, enableStorageClassDeletionAnnotation},
|
||||
capsulev1beta1.IngressClassesProxy: {enableIngressClassListingAnnotation, enableIngressClassUpdateAnnotation, enableIngressClassDeletionAnnotation},
|
||||
capsulev1beta1.PriorityClassesProxy: {enablePriorityClassListingAnnotation, enablePriorityClassUpdateAnnotation, enablePriorityClassDeletionAnnotation},
|
||||
}
|
||||
var annotationToOperationMap = map[string]capsulev1beta1.ProxyOperation{
|
||||
enableNodeListingAnnotation: capsulev1beta1.ListOperation,
|
||||
enableNodeUpdateAnnotation: capsulev1beta1.UpdateOperation,
|
||||
enableNodeDeletionAnnotation: capsulev1beta1.DeleteOperation,
|
||||
enableStorageClassListingAnnotation: capsulev1beta1.ListOperation,
|
||||
enableStorageClassUpdateAnnotation: capsulev1beta1.UpdateOperation,
|
||||
enableStorageClassDeletionAnnotation: capsulev1beta1.DeleteOperation,
|
||||
enableIngressClassListingAnnotation: capsulev1beta1.ListOperation,
|
||||
enableIngressClassUpdateAnnotation: capsulev1beta1.UpdateOperation,
|
||||
enableIngressClassDeletionAnnotation: capsulev1beta1.DeleteOperation,
|
||||
enablePriorityClassListingAnnotation: capsulev1beta1.ListOperation,
|
||||
enablePriorityClassUpdateAnnotation: capsulev1beta1.UpdateOperation,
|
||||
enablePriorityClassDeletionAnnotation: capsulev1beta1.DeleteOperation,
|
||||
}
|
||||
var annotationToOwnerKindMap = map[string]capsulev1beta1.OwnerKind{
|
||||
ownerUsersAnnotation: capsulev1beta1.UserOwner,
|
||||
ownerGroupsAnnotation: capsulev1beta1.GroupOwner,
|
||||
ownerServiceAccountAnnotation: capsulev1beta1.ServiceAccountOwner,
|
||||
}
|
||||
annotations := t.GetAnnotations()
|
||||
|
||||
var operations = make(map[string]map[capsulev1beta1.ProxyServiceKind][]capsulev1beta1.ProxyOperation)
|
||||
|
||||
for serviceKind, operationAnnotations := range serviceKindToAnnotationMap {
|
||||
for _, operationAnnotation := range operationAnnotations {
|
||||
val, ok := annotations[operationAnnotation]
|
||||
if ok {
|
||||
for _, owner := range strings.Split(val, ",") {
|
||||
if _, exists := operations[owner]; !exists {
|
||||
operations[owner] = make(map[capsulev1beta1.ProxyServiceKind][]capsulev1beta1.ProxyOperation)
|
||||
}
|
||||
operations[owner][serviceKind] = append(operations[owner][serviceKind], annotationToOperationMap[operationAnnotation])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var owners capsulev1beta1.OwnerListSpec
|
||||
|
||||
var getProxySettingsForOwner = func(ownerName string) (settings []capsulev1beta1.ProxySettings) {
|
||||
ownerOperations, ok := operations[ownerName]
|
||||
if ok {
|
||||
for k, v := range ownerOperations {
|
||||
settings = append(settings, capsulev1beta1.ProxySettings{
|
||||
Kind: k,
|
||||
Operations: v,
|
||||
})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
owners = append(owners, capsulev1beta1.OwnerSpec{
|
||||
Kind: capsulev1beta1.OwnerKind(t.Spec.Owner.Kind),
|
||||
Name: t.Spec.Owner.Name,
|
||||
ProxyOperations: getProxySettingsForOwner(t.Spec.Owner.Name),
|
||||
})
|
||||
|
||||
for ownerAnnotation, ownerKind := range annotationToOwnerKindMap {
|
||||
val, ok := annotations[ownerAnnotation]
|
||||
if ok {
|
||||
for _, owner := range strings.Split(val, ",") {
|
||||
owners = append(owners, capsulev1beta1.OwnerSpec{
|
||||
Kind: ownerKind,
|
||||
Name: owner,
|
||||
ProxyOperations: getProxySettingsForOwner(owner),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return owners
|
||||
}
|
||||
|
||||
func (t *Tenant) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst := dstRaw.(*capsulev1beta1.Tenant)
|
||||
annotations := t.GetAnnotations()
|
||||
|
||||
// ObjectMeta
|
||||
dst.ObjectMeta = t.ObjectMeta
|
||||
|
||||
// Spec
|
||||
if t.Spec.NamespaceQuota != nil {
|
||||
if dst.Spec.NamespaceOptions == nil {
|
||||
dst.Spec.NamespaceOptions = &capsulev1beta1.NamespaceOptions{}
|
||||
}
|
||||
dst.Spec.NamespaceOptions.Quota = t.Spec.NamespaceQuota
|
||||
}
|
||||
|
||||
dst.Spec.NodeSelector = t.Spec.NodeSelector
|
||||
|
||||
dst.Spec.Owners = t.convertV1Alpha1OwnerToV1Beta1()
|
||||
|
||||
if t.Spec.NamespacesMetadata != nil {
|
||||
if dst.Spec.NamespaceOptions == nil {
|
||||
dst.Spec.NamespaceOptions = &capsulev1beta1.NamespaceOptions{}
|
||||
}
|
||||
dst.Spec.NamespaceOptions.AdditionalMetadata = &capsulev1beta1.AdditionalMetadataSpec{
|
||||
Labels: t.Spec.NamespacesMetadata.AdditionalLabels,
|
||||
Annotations: t.Spec.NamespacesMetadata.AdditionalAnnotations,
|
||||
}
|
||||
}
|
||||
if t.Spec.ServicesMetadata != nil {
|
||||
if dst.Spec.ServiceOptions == nil {
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{
|
||||
AdditionalMetadata: &capsulev1beta1.AdditionalMetadataSpec{
|
||||
Labels: t.Spec.ServicesMetadata.AdditionalLabels,
|
||||
Annotations: t.Spec.ServicesMetadata.AdditionalAnnotations,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if t.Spec.StorageClasses != nil {
|
||||
dst.Spec.StorageClasses = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.StorageClasses.Exact,
|
||||
Regex: t.Spec.StorageClasses.Regex,
|
||||
}
|
||||
}
|
||||
if v, ok := t.Annotations[ingressHostnameCollisionScope]; ok {
|
||||
switch v {
|
||||
case string(capsulev1beta1.HostnameCollisionScopeCluster), string(capsulev1beta1.HostnameCollisionScopeTenant), string(capsulev1beta1.HostnameCollisionScopeNamespace):
|
||||
dst.Spec.IngressOptions.HostnameCollisionScope = capsulev1beta1.HostnameCollisionScope(v)
|
||||
default:
|
||||
dst.Spec.IngressOptions.HostnameCollisionScope = capsulev1beta1.HostnameCollisionScopeDisabled
|
||||
}
|
||||
}
|
||||
if t.Spec.IngressClasses != nil {
|
||||
dst.Spec.IngressOptions.AllowedClasses = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.IngressClasses.Exact,
|
||||
Regex: t.Spec.IngressClasses.Regex,
|
||||
}
|
||||
}
|
||||
if t.Spec.IngressHostnames != nil {
|
||||
dst.Spec.IngressOptions.AllowedHostnames = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.IngressHostnames.Exact,
|
||||
Regex: t.Spec.IngressHostnames.Regex,
|
||||
}
|
||||
}
|
||||
if t.Spec.ContainerRegistries != nil {
|
||||
dst.Spec.ContainerRegistries = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.ContainerRegistries.Exact,
|
||||
Regex: t.Spec.ContainerRegistries.Regex,
|
||||
}
|
||||
}
|
||||
if len(t.Spec.NetworkPolicies) > 0 {
|
||||
dst.Spec.NetworkPolicies = capsulev1beta1.NetworkPolicySpec{
|
||||
Items: t.Spec.NetworkPolicies,
|
||||
}
|
||||
}
|
||||
if len(t.Spec.LimitRanges) > 0 {
|
||||
dst.Spec.LimitRanges = capsulev1beta1.LimitRangesSpec{
|
||||
Items: t.Spec.LimitRanges,
|
||||
}
|
||||
}
|
||||
if len(t.Spec.ResourceQuota) > 0 {
|
||||
dst.Spec.ResourceQuota = capsulev1beta1.ResourceQuotaSpec{
|
||||
Scope: func() capsulev1beta1.ResourceQuotaScope {
|
||||
if v, ok := t.GetAnnotations()[resourceQuotaScopeAnnotation]; ok {
|
||||
switch v {
|
||||
case string(capsulev1beta1.ResourceQuotaScopeNamespace):
|
||||
return capsulev1beta1.ResourceQuotaScopeNamespace
|
||||
case string(capsulev1beta1.ResourceQuotaScopeTenant):
|
||||
return capsulev1beta1.ResourceQuotaScopeTenant
|
||||
}
|
||||
}
|
||||
return capsulev1beta1.ResourceQuotaScopeTenant
|
||||
}(),
|
||||
Items: t.Spec.ResourceQuota,
|
||||
}
|
||||
}
|
||||
if len(t.Spec.AdditionalRoleBindings) > 0 {
|
||||
for _, rb := range t.Spec.AdditionalRoleBindings {
|
||||
dst.Spec.AdditionalRoleBindings = append(dst.Spec.AdditionalRoleBindings, capsulev1beta1.AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: rb.ClusterRoleName,
|
||||
Subjects: rb.Subjects,
|
||||
})
|
||||
}
|
||||
}
|
||||
if t.Spec.ExternalServiceIPs != nil {
|
||||
if dst.Spec.ServiceOptions == nil {
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
dst.Spec.ServiceOptions.ExternalServiceIPs = &capsulev1beta1.ExternalServiceIPsSpec{
|
||||
Allowed: make([]capsulev1beta1.AllowedIP, len(t.Spec.ExternalServiceIPs.Allowed)),
|
||||
}
|
||||
|
||||
for i, IP := range t.Spec.ExternalServiceIPs.Allowed {
|
||||
dst.Spec.ServiceOptions.ExternalServiceIPs.Allowed[i] = capsulev1beta1.AllowedIP(IP)
|
||||
}
|
||||
}
|
||||
|
||||
pullPolicies, ok := annotations[podAllowedImagePullPolicyAnnotation]
|
||||
if ok {
|
||||
for _, policy := range strings.Split(pullPolicies, ",") {
|
||||
dst.Spec.ImagePullPolicies = append(dst.Spec.ImagePullPolicies, capsulev1beta1.ImagePullPolicySpec(policy))
|
||||
}
|
||||
}
|
||||
|
||||
priorityClasses := capsulev1beta1.AllowedListSpec{}
|
||||
|
||||
priorityClassAllowed, ok := annotations[podPriorityAllowedAnnotation]
|
||||
if ok {
|
||||
priorityClasses.Exact = strings.Split(priorityClassAllowed, ",")
|
||||
}
|
||||
priorityClassesRegexp, ok := annotations[podPriorityAllowedRegexAnnotation]
|
||||
if ok {
|
||||
priorityClasses.Regex = priorityClassesRegexp
|
||||
}
|
||||
|
||||
if !reflect.ValueOf(priorityClasses).IsZero() {
|
||||
dst.Spec.PriorityClasses = &priorityClasses
|
||||
}
|
||||
|
||||
enableNodePorts, ok := annotations[enableNodePortsAnnotation]
|
||||
if ok {
|
||||
val, err := strconv.ParseBool(enableNodePorts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("unable to parse %s annotation on tenant %s", enableNodePortsAnnotation, t.GetName()))
|
||||
}
|
||||
if dst.Spec.ServiceOptions == nil {
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
if dst.Spec.ServiceOptions.AllowedServices == nil {
|
||||
dst.Spec.ServiceOptions.AllowedServices = &capsulev1beta1.AllowedServices{}
|
||||
}
|
||||
dst.Spec.ServiceOptions.AllowedServices.NodePort = pointer.BoolPtr(val)
|
||||
}
|
||||
|
||||
enableExternalName, ok := annotations[enableExternalNameAnnotation]
|
||||
if ok {
|
||||
val, err := strconv.ParseBool(enableExternalName)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("unable to parse %s annotation on tenant %s", enableExternalNameAnnotation, t.GetName()))
|
||||
}
|
||||
if dst.Spec.ServiceOptions == nil {
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
if dst.Spec.ServiceOptions.AllowedServices == nil {
|
||||
dst.Spec.ServiceOptions.AllowedServices = &capsulev1beta1.AllowedServices{}
|
||||
}
|
||||
dst.Spec.ServiceOptions.AllowedServices.ExternalName = pointer.BoolPtr(val)
|
||||
}
|
||||
|
||||
loadBalancerService, ok := annotations[enableLoadBalancerAnnotation]
|
||||
if ok {
|
||||
val, err := strconv.ParseBool(loadBalancerService)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, fmt.Sprintf("unable to parse %s annotation on tenant %s", enableLoadBalancerAnnotation, t.GetName()))
|
||||
}
|
||||
if dst.Spec.ServiceOptions == nil {
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
if dst.Spec.ServiceOptions.AllowedServices == nil {
|
||||
dst.Spec.ServiceOptions.AllowedServices = &capsulev1beta1.AllowedServices{}
|
||||
}
|
||||
dst.Spec.ServiceOptions.AllowedServices.LoadBalancer = pointer.BoolPtr(val)
|
||||
}
|
||||
|
||||
// Status
|
||||
dst.Status = capsulev1beta1.TenantStatus{
|
||||
Size: t.Status.Size,
|
||||
Namespaces: t.Status.Namespaces,
|
||||
}
|
||||
|
||||
// Remove unneeded annotations
|
||||
delete(dst.ObjectMeta.Annotations, podAllowedImagePullPolicyAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, podPriorityAllowedAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, podPriorityAllowedRegexAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableNodePortsAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableExternalNameAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableLoadBalancerAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, ownerGroupsAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, ownerUsersAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, ownerServiceAccountAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableNodeListingAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableNodeUpdateAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableNodeDeletionAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableStorageClassListingAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableStorageClassUpdateAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableStorageClassDeletionAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableIngressClassListingAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableIngressClassUpdateAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enableIngressClassDeletionAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enablePriorityClassListingAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enablePriorityClassUpdateAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, enablePriorityClassDeletionAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, resourceQuotaScopeAnnotation)
|
||||
delete(dst.ObjectMeta.Annotations, ingressHostnameCollisionScope)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Tenant) convertV1Beta1OwnerToV1Alpha1(src *capsulev1beta1.Tenant) {
|
||||
var ownersAnnotations = map[string][]string{
|
||||
ownerGroupsAnnotation: nil,
|
||||
ownerUsersAnnotation: nil,
|
||||
ownerServiceAccountAnnotation: nil,
|
||||
}
|
||||
|
||||
var proxyAnnotations = map[string][]string{
|
||||
enableNodeListingAnnotation: nil,
|
||||
enableNodeUpdateAnnotation: nil,
|
||||
enableNodeDeletionAnnotation: nil,
|
||||
enableStorageClassListingAnnotation: nil,
|
||||
enableStorageClassUpdateAnnotation: nil,
|
||||
enableStorageClassDeletionAnnotation: nil,
|
||||
enableIngressClassListingAnnotation: nil,
|
||||
enableIngressClassUpdateAnnotation: nil,
|
||||
enableIngressClassDeletionAnnotation: nil,
|
||||
}
|
||||
|
||||
for i, owner := range src.Spec.Owners {
|
||||
if i == 0 {
|
||||
t.Spec.Owner = OwnerSpec{
|
||||
Name: owner.Name,
|
||||
Kind: Kind(owner.Kind),
|
||||
}
|
||||
} else {
|
||||
switch owner.Kind {
|
||||
case capsulev1beta1.UserOwner:
|
||||
ownersAnnotations[ownerUsersAnnotation] = append(ownersAnnotations[ownerUsersAnnotation], owner.Name)
|
||||
case capsulev1beta1.GroupOwner:
|
||||
ownersAnnotations[ownerGroupsAnnotation] = append(ownersAnnotations[ownerGroupsAnnotation], owner.Name)
|
||||
case capsulev1beta1.ServiceAccountOwner:
|
||||
ownersAnnotations[ownerServiceAccountAnnotation] = append(ownersAnnotations[ownerServiceAccountAnnotation], owner.Name)
|
||||
}
|
||||
}
|
||||
for _, setting := range owner.ProxyOperations {
|
||||
switch setting.Kind {
|
||||
case capsulev1beta1.NodesProxy:
|
||||
for _, operation := range setting.Operations {
|
||||
switch operation {
|
||||
case capsulev1beta1.ListOperation:
|
||||
proxyAnnotations[enableNodeListingAnnotation] = append(proxyAnnotations[enableNodeListingAnnotation], owner.Name)
|
||||
case capsulev1beta1.UpdateOperation:
|
||||
proxyAnnotations[enableNodeUpdateAnnotation] = append(proxyAnnotations[enableNodeUpdateAnnotation], owner.Name)
|
||||
case capsulev1beta1.DeleteOperation:
|
||||
proxyAnnotations[enableNodeDeletionAnnotation] = append(proxyAnnotations[enableNodeDeletionAnnotation], owner.Name)
|
||||
}
|
||||
}
|
||||
case capsulev1beta1.PriorityClassesProxy:
|
||||
for _, operation := range setting.Operations {
|
||||
switch operation {
|
||||
case capsulev1beta1.ListOperation:
|
||||
proxyAnnotations[enablePriorityClassListingAnnotation] = append(proxyAnnotations[enablePriorityClassListingAnnotation], owner.Name)
|
||||
case capsulev1beta1.UpdateOperation:
|
||||
proxyAnnotations[enablePriorityClassUpdateAnnotation] = append(proxyAnnotations[enablePriorityClassUpdateAnnotation], owner.Name)
|
||||
case capsulev1beta1.DeleteOperation:
|
||||
proxyAnnotations[enablePriorityClassDeletionAnnotation] = append(proxyAnnotations[enablePriorityClassDeletionAnnotation], owner.Name)
|
||||
}
|
||||
}
|
||||
case capsulev1beta1.StorageClassesProxy:
|
||||
for _, operation := range setting.Operations {
|
||||
switch operation {
|
||||
case capsulev1beta1.ListOperation:
|
||||
proxyAnnotations[enableStorageClassListingAnnotation] = append(proxyAnnotations[enableStorageClassListingAnnotation], owner.Name)
|
||||
case capsulev1beta1.UpdateOperation:
|
||||
proxyAnnotations[enableStorageClassUpdateAnnotation] = append(proxyAnnotations[enableStorageClassUpdateAnnotation], owner.Name)
|
||||
case capsulev1beta1.DeleteOperation:
|
||||
proxyAnnotations[enableStorageClassDeletionAnnotation] = append(proxyAnnotations[enableStorageClassDeletionAnnotation], owner.Name)
|
||||
}
|
||||
}
|
||||
case capsulev1beta1.IngressClassesProxy:
|
||||
for _, operation := range setting.Operations {
|
||||
switch operation {
|
||||
case capsulev1beta1.ListOperation:
|
||||
proxyAnnotations[enableIngressClassListingAnnotation] = append(proxyAnnotations[enableIngressClassListingAnnotation], owner.Name)
|
||||
case capsulev1beta1.UpdateOperation:
|
||||
proxyAnnotations[enableIngressClassUpdateAnnotation] = append(proxyAnnotations[enableIngressClassUpdateAnnotation], owner.Name)
|
||||
case capsulev1beta1.DeleteOperation:
|
||||
proxyAnnotations[enableIngressClassDeletionAnnotation] = append(proxyAnnotations[enableIngressClassDeletionAnnotation], owner.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range ownersAnnotations {
|
||||
if len(v) > 0 {
|
||||
t.Annotations[k] = strings.Join(v, ",")
|
||||
}
|
||||
}
|
||||
for k, v := range proxyAnnotations {
|
||||
if len(v) > 0 {
|
||||
t.Annotations[k] = strings.Join(v, ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tenant) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src := srcRaw.(*capsulev1beta1.Tenant)
|
||||
|
||||
// ObjectMeta
|
||||
t.ObjectMeta = src.ObjectMeta
|
||||
|
||||
// Spec
|
||||
if src.Spec.NamespaceOptions != nil && src.Spec.NamespaceOptions.Quota != nil {
|
||||
t.Spec.NamespaceQuota = src.Spec.NamespaceOptions.Quota
|
||||
}
|
||||
|
||||
t.Spec.NodeSelector = src.Spec.NodeSelector
|
||||
|
||||
if t.Annotations == nil {
|
||||
t.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
t.convertV1Beta1OwnerToV1Alpha1(src)
|
||||
|
||||
if src.Spec.NamespaceOptions != nil && src.Spec.NamespaceOptions.AdditionalMetadata != nil {
|
||||
t.Spec.NamespacesMetadata = &AdditionalMetadataSpec{
|
||||
AdditionalLabels: src.Spec.NamespaceOptions.AdditionalMetadata.Labels,
|
||||
AdditionalAnnotations: src.Spec.NamespaceOptions.AdditionalMetadata.Annotations,
|
||||
}
|
||||
}
|
||||
if src.Spec.ServiceOptions != nil && src.Spec.ServiceOptions.AdditionalMetadata != nil {
|
||||
t.Spec.ServicesMetadata = &AdditionalMetadataSpec{
|
||||
AdditionalLabels: src.Spec.ServiceOptions.AdditionalMetadata.Labels,
|
||||
AdditionalAnnotations: src.Spec.ServiceOptions.AdditionalMetadata.Annotations,
|
||||
}
|
||||
}
|
||||
if src.Spec.StorageClasses != nil {
|
||||
t.Spec.StorageClasses = &AllowedListSpec{
|
||||
Exact: src.Spec.StorageClasses.Exact,
|
||||
Regex: src.Spec.StorageClasses.Regex,
|
||||
}
|
||||
}
|
||||
t.Annotations[ingressHostnameCollisionScope] = string(src.Spec.IngressOptions.HostnameCollisionScope)
|
||||
if src.Spec.IngressOptions.AllowedClasses != nil {
|
||||
t.Spec.IngressClasses = &AllowedListSpec{
|
||||
Exact: src.Spec.IngressOptions.AllowedClasses.Exact,
|
||||
Regex: src.Spec.IngressOptions.AllowedClasses.Regex,
|
||||
}
|
||||
}
|
||||
if src.Spec.IngressOptions.AllowedHostnames != nil {
|
||||
t.Spec.IngressHostnames = &AllowedListSpec{
|
||||
Exact: src.Spec.IngressOptions.AllowedHostnames.Exact,
|
||||
Regex: src.Spec.IngressOptions.AllowedHostnames.Regex,
|
||||
}
|
||||
}
|
||||
if src.Spec.ContainerRegistries != nil {
|
||||
t.Spec.ContainerRegistries = &AllowedListSpec{
|
||||
Exact: src.Spec.ContainerRegistries.Exact,
|
||||
Regex: src.Spec.ContainerRegistries.Regex,
|
||||
}
|
||||
}
|
||||
if len(src.Spec.NetworkPolicies.Items) > 0 {
|
||||
t.Spec.NetworkPolicies = src.Spec.NetworkPolicies.Items
|
||||
}
|
||||
if len(src.Spec.LimitRanges.Items) > 0 {
|
||||
t.Spec.LimitRanges = src.Spec.LimitRanges.Items
|
||||
}
|
||||
if len(src.Spec.ResourceQuota.Items) > 0 {
|
||||
t.Annotations[resourceQuotaScopeAnnotation] = string(src.Spec.ResourceQuota.Scope)
|
||||
t.Spec.ResourceQuota = src.Spec.ResourceQuota.Items
|
||||
}
|
||||
if len(src.Spec.AdditionalRoleBindings) > 0 {
|
||||
for _, rb := range src.Spec.AdditionalRoleBindings {
|
||||
t.Spec.AdditionalRoleBindings = append(t.Spec.AdditionalRoleBindings, AdditionalRoleBindingsSpec{
|
||||
ClusterRoleName: rb.ClusterRoleName,
|
||||
Subjects: rb.Subjects,
|
||||
})
|
||||
}
|
||||
}
|
||||
if src.Spec.ServiceOptions != nil && src.Spec.ServiceOptions.ExternalServiceIPs != nil {
|
||||
t.Spec.ExternalServiceIPs = &ExternalServiceIPsSpec{
|
||||
Allowed: make([]AllowedIP, len(src.Spec.ServiceOptions.ExternalServiceIPs.Allowed)),
|
||||
}
|
||||
|
||||
for i, IP := range src.Spec.ServiceOptions.ExternalServiceIPs.Allowed {
|
||||
t.Spec.ExternalServiceIPs.Allowed[i] = AllowedIP(IP)
|
||||
}
|
||||
}
|
||||
if len(src.Spec.ImagePullPolicies) != 0 {
|
||||
var pullPolicies []string
|
||||
for _, policy := range src.Spec.ImagePullPolicies {
|
||||
pullPolicies = append(pullPolicies, string(policy))
|
||||
}
|
||||
t.Annotations[podAllowedImagePullPolicyAnnotation] = strings.Join(pullPolicies, ",")
|
||||
}
|
||||
|
||||
if src.Spec.PriorityClasses != nil {
|
||||
if len(src.Spec.PriorityClasses.Exact) != 0 {
|
||||
t.Annotations[podPriorityAllowedAnnotation] = strings.Join(src.Spec.PriorityClasses.Exact, ",")
|
||||
}
|
||||
if src.Spec.PriorityClasses.Regex != "" {
|
||||
t.Annotations[podPriorityAllowedRegexAnnotation] = src.Spec.PriorityClasses.Regex
|
||||
}
|
||||
}
|
||||
|
||||
if src.Spec.ServiceOptions != nil && src.Spec.ServiceOptions.AllowedServices != nil {
|
||||
t.Annotations[enableNodePortsAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.NodePort)
|
||||
t.Annotations[enableExternalNameAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.ExternalName)
|
||||
t.Annotations[enableLoadBalancerAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.LoadBalancer)
|
||||
}
|
||||
|
||||
// Status
|
||||
t.Status = TenantStatus{
|
||||
Size: src.Status.Size,
|
||||
Namespaces: src.Status.Namespaces,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
384
api/v1alpha1/conversion_hub_test.go
Normal file
384
api/v1alpha1/conversion_hub_test.go
Normal file
@@ -0,0 +1,384 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
||||
var namespaceQuota int32 = 5
|
||||
var nodeSelector = map[string]string{
|
||||
"foo": "bar",
|
||||
}
|
||||
var v1alpha1AdditionalMetadataSpec = &AdditionalMetadataSpec{
|
||||
AdditionalLabels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
AdditionalAnnotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
var v1alpha1AllowedListSpec = &AllowedListSpec{
|
||||
Exact: []string{"foo", "bar"},
|
||||
Regex: "^foo*",
|
||||
}
|
||||
var v1beta1AdditionalMetadataSpec = &capsulev1beta1.AdditionalMetadataSpec{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
var v1beta1NamespaceOptions = &capsulev1beta1.NamespaceOptions{
|
||||
Quota: &namespaceQuota,
|
||||
AdditionalMetadata: v1beta1AdditionalMetadataSpec,
|
||||
}
|
||||
var v1beta1ServiceOptions = &capsulev1beta1.ServiceOptions{
|
||||
AdditionalMetadata: v1beta1AdditionalMetadataSpec,
|
||||
AllowedServices: &capsulev1beta1.AllowedServices{
|
||||
NodePort: pointer.BoolPtr(false),
|
||||
ExternalName: pointer.BoolPtr(false),
|
||||
LoadBalancer: pointer.BoolPtr(false),
|
||||
},
|
||||
ExternalServiceIPs: &capsulev1beta1.ExternalServiceIPsSpec{
|
||||
Allowed: []capsulev1beta1.AllowedIP{"192.168.0.1"},
|
||||
},
|
||||
}
|
||||
var v1beta1AllowedListSpec = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"foo", "bar"},
|
||||
Regex: "^foo*",
|
||||
}
|
||||
var networkPolicies = []networkingv1.NetworkPolicySpec{
|
||||
{
|
||||
Ingress: []networkingv1.NetworkPolicyIngressRule{
|
||||
{
|
||||
From: []networkingv1.NetworkPolicyPeer{
|
||||
{
|
||||
NamespaceSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"foo": "tenant-resources",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
PodSelector: &metav1.LabelSelector{},
|
||||
},
|
||||
{
|
||||
IPBlock: &networkingv1.IPBlock{
|
||||
CIDR: "192.168.0.0/12",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var limitRanges = []corev1.LimitRangeSpec{
|
||||
{
|
||||
Limits: []corev1.LimitRangeItem{
|
||||
{
|
||||
Type: corev1.LimitTypePod,
|
||||
Min: map[corev1.ResourceName]resource.Quantity{
|
||||
corev1.ResourceCPU: resource.MustParse("50m"),
|
||||
corev1.ResourceMemory: resource.MustParse("5Mi"),
|
||||
},
|
||||
Max: map[corev1.ResourceName]resource.Quantity{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
var resourceQuotas = []corev1.ResourceQuotaSpec{
|
||||
{
|
||||
Hard: map[corev1.ResourceName]resource.Quantity{
|
||||
corev1.ResourceLimitsCPU: resource.MustParse("8"),
|
||||
corev1.ResourceLimitsMemory: resource.MustParse("16Gi"),
|
||||
corev1.ResourceRequestsCPU: resource.MustParse("8"),
|
||||
corev1.ResourceRequestsMemory: resource.MustParse("16Gi"),
|
||||
},
|
||||
Scopes: []corev1.ResourceQuotaScope{
|
||||
corev1.ResourceQuotaScopeNotTerminating,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var v1beta1Tnt = capsulev1beta1.Tenant{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "alice",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
Spec: capsulev1beta1.TenantSpec{
|
||||
Owners: capsulev1beta1.OwnerListSpec{
|
||||
{
|
||||
Kind: "User",
|
||||
Name: "alice",
|
||||
ProxyOperations: []capsulev1beta1.ProxySettings{
|
||||
{
|
||||
Kind: "IngressClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"List", "Update", "Delete"},
|
||||
},
|
||||
{
|
||||
Kind: "Nodes",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Update", "Delete"},
|
||||
},
|
||||
{
|
||||
Kind: "StorageClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Update", "Delete"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "User",
|
||||
Name: "bob",
|
||||
ProxyOperations: []capsulev1beta1.ProxySettings{
|
||||
{
|
||||
Kind: "IngressClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Update"},
|
||||
},
|
||||
{
|
||||
Kind: "StorageClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "User",
|
||||
Name: "jack",
|
||||
ProxyOperations: []capsulev1beta1.ProxySettings{
|
||||
{
|
||||
Kind: "IngressClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Delete"},
|
||||
},
|
||||
{
|
||||
Kind: "Nodes",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Delete"},
|
||||
},
|
||||
{
|
||||
Kind: "StorageClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
||||
},
|
||||
{
|
||||
Kind: "PriorityClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "Group",
|
||||
Name: "owner-foo",
|
||||
ProxyOperations: []capsulev1beta1.ProxySettings{
|
||||
{
|
||||
Kind: "IngressClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "Group",
|
||||
Name: "owner-bar",
|
||||
ProxyOperations: []capsulev1beta1.ProxySettings{
|
||||
{
|
||||
Kind: "IngressClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"List"},
|
||||
},
|
||||
{
|
||||
Kind: "StorageClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Delete"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
Name: "system:serviceaccount:oil-production:default",
|
||||
ProxyOperations: []capsulev1beta1.ProxySettings{
|
||||
{
|
||||
Kind: "Nodes",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Update"},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
Name: "system:serviceaccount:gas-production:gas",
|
||||
ProxyOperations: []capsulev1beta1.ProxySettings{
|
||||
{
|
||||
Kind: "StorageClasses",
|
||||
Operations: []capsulev1beta1.ProxyOperation{"Update"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NamespaceOptions: v1beta1NamespaceOptions,
|
||||
ServiceOptions: v1beta1ServiceOptions,
|
||||
StorageClasses: v1beta1AllowedListSpec,
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
HostnameCollisionScope: capsulev1beta1.HostnameCollisionScopeDisabled,
|
||||
AllowedClasses: v1beta1AllowedListSpec,
|
||||
AllowedHostnames: v1beta1AllowedListSpec,
|
||||
},
|
||||
ContainerRegistries: v1beta1AllowedListSpec,
|
||||
NodeSelector: nodeSelector,
|
||||
NetworkPolicies: capsulev1beta1.NetworkPolicySpec{
|
||||
Items: networkPolicies,
|
||||
},
|
||||
LimitRanges: capsulev1beta1.LimitRangesSpec{
|
||||
Items: limitRanges,
|
||||
},
|
||||
ResourceQuota: capsulev1beta1.ResourceQuotaSpec{
|
||||
Scope: capsulev1beta1.ResourceQuotaScopeNamespace,
|
||||
Items: resourceQuotas,
|
||||
},
|
||||
AdditionalRoleBindings: []capsulev1beta1.AdditionalRoleBindingsSpec{
|
||||
{
|
||||
ClusterRoleName: "crds-rolebinding",
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: "Group",
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Name: "system:authenticated",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ImagePullPolicies: []capsulev1beta1.ImagePullPolicySpec{"Always", "IfNotPresent"},
|
||||
PriorityClasses: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"default"},
|
||||
Regex: "^tier-.*$",
|
||||
},
|
||||
},
|
||||
Status: capsulev1beta1.TenantStatus{
|
||||
Size: 1,
|
||||
Namespaces: []string{"foo", "bar"},
|
||||
},
|
||||
}
|
||||
|
||||
var v1alpha1Tnt = Tenant{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "alice",
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"foo": "bar",
|
||||
podAllowedImagePullPolicyAnnotation: "Always,IfNotPresent",
|
||||
enableExternalNameAnnotation: "false",
|
||||
enableNodePortsAnnotation: "false",
|
||||
enableLoadBalancerAnnotation: "false",
|
||||
podPriorityAllowedAnnotation: "default",
|
||||
podPriorityAllowedRegexAnnotation: "^tier-.*$",
|
||||
ownerGroupsAnnotation: "owner-foo,owner-bar",
|
||||
ownerUsersAnnotation: "bob,jack",
|
||||
ownerServiceAccountAnnotation: "system:serviceaccount:oil-production:default,system:serviceaccount:gas-production:gas",
|
||||
enableNodeUpdateAnnotation: "alice,system:serviceaccount:oil-production:default",
|
||||
enableNodeDeletionAnnotation: "alice,jack",
|
||||
enableStorageClassListingAnnotation: "bob,jack",
|
||||
enableStorageClassUpdateAnnotation: "alice,system:serviceaccount:gas-production:gas",
|
||||
enableStorageClassDeletionAnnotation: "alice,owner-bar",
|
||||
enableIngressClassListingAnnotation: "alice,owner-foo,owner-bar",
|
||||
enableIngressClassUpdateAnnotation: "alice,bob",
|
||||
enableIngressClassDeletionAnnotation: "alice,jack",
|
||||
enablePriorityClassListingAnnotation: "jack",
|
||||
resourceQuotaScopeAnnotation: "Namespace",
|
||||
ingressHostnameCollisionScope: "Disabled",
|
||||
},
|
||||
},
|
||||
Spec: TenantSpec{
|
||||
Owner: OwnerSpec{
|
||||
Name: "alice",
|
||||
Kind: "User",
|
||||
},
|
||||
NamespaceQuota: &namespaceQuota,
|
||||
NamespacesMetadata: v1alpha1AdditionalMetadataSpec,
|
||||
ServicesMetadata: v1alpha1AdditionalMetadataSpec,
|
||||
StorageClasses: v1alpha1AllowedListSpec,
|
||||
IngressClasses: v1alpha1AllowedListSpec,
|
||||
IngressHostnames: v1alpha1AllowedListSpec,
|
||||
ContainerRegistries: v1alpha1AllowedListSpec,
|
||||
NodeSelector: nodeSelector,
|
||||
NetworkPolicies: networkPolicies,
|
||||
LimitRanges: limitRanges,
|
||||
ResourceQuota: resourceQuotas,
|
||||
AdditionalRoleBindings: []AdditionalRoleBindingsSpec{
|
||||
{
|
||||
ClusterRoleName: "crds-rolebinding",
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: "Group",
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Name: "system:authenticated",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExternalServiceIPs: &ExternalServiceIPsSpec{
|
||||
Allowed: []AllowedIP{"192.168.0.1"},
|
||||
},
|
||||
},
|
||||
Status: TenantStatus{
|
||||
Size: 1,
|
||||
Namespaces: []string{"foo", "bar"},
|
||||
},
|
||||
}
|
||||
|
||||
return v1alpha1Tnt, v1beta1Tnt
|
||||
}
|
||||
|
||||
func TestConversionHub_ConvertTo(t *testing.T) {
|
||||
var v1beta1ConvertedTnt = capsulev1beta1.Tenant{}
|
||||
|
||||
v1alpha1Tnt, v1beta1tnt := generateTenantsSpecs()
|
||||
err := v1alpha1Tnt.ConvertTo(&v1beta1ConvertedTnt)
|
||||
if assert.NoError(t, err) {
|
||||
sort.Slice(v1beta1tnt.Spec.Owners, func(i, j int) bool {
|
||||
return v1beta1tnt.Spec.Owners[i].Name < v1beta1tnt.Spec.Owners[j].Name
|
||||
})
|
||||
sort.Slice(v1beta1ConvertedTnt.Spec.Owners, func(i, j int) bool {
|
||||
return v1beta1ConvertedTnt.Spec.Owners[i].Name < v1beta1ConvertedTnt.Spec.Owners[j].Name
|
||||
})
|
||||
|
||||
for _, owner := range v1beta1tnt.Spec.Owners {
|
||||
sort.Slice(owner.ProxyOperations, func(i, j int) bool {
|
||||
return owner.ProxyOperations[i].Kind < owner.ProxyOperations[j].Kind
|
||||
})
|
||||
}
|
||||
for _, owner := range v1beta1ConvertedTnt.Spec.Owners {
|
||||
sort.Slice(owner.ProxyOperations, func(i, j int) bool {
|
||||
return owner.ProxyOperations[i].Kind < owner.ProxyOperations[j].Kind
|
||||
})
|
||||
}
|
||||
assert.Equal(t, v1beta1tnt, v1beta1ConvertedTnt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConversionHub_ConvertFrom(t *testing.T) {
|
||||
var v1alpha1ConvertedTnt = Tenant{}
|
||||
v1alpha1Tnt, v1beta1tnt := generateTenantsSpecs()
|
||||
|
||||
err := v1alpha1ConvertedTnt.ConvertFrom(&v1beta1tnt)
|
||||
if assert.NoError(t, err) {
|
||||
assert.EqualValues(t, v1alpha1Tnt, v1alpha1ConvertedTnt)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package domain
|
||||
|
||||
type AllowedList interface {
|
||||
ExactMatch(value string) bool
|
||||
RegexMatch(value string) bool
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package domain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewRegistry(t *testing.T) {
|
||||
type tc struct {
|
||||
registry string
|
||||
repo string
|
||||
image string
|
||||
tag string
|
||||
}
|
||||
for name, tc := range map[string]tc{
|
||||
"docker.io/my-org/my-repo:v0.0.1": {
|
||||
registry: "docker.io",
|
||||
repo: "my-org",
|
||||
image: "my-repo",
|
||||
tag: "v0.0.1",
|
||||
},
|
||||
"unnamed/repository:1.2.3": {
|
||||
registry: "docker.io",
|
||||
repo: "unnamed",
|
||||
image: "repository",
|
||||
tag: "1.2.3",
|
||||
},
|
||||
"quay.io/clastix/capsule:v1.0.0": {
|
||||
registry: "quay.io",
|
||||
repo: "clastix",
|
||||
image: "capsule",
|
||||
tag: "v1.0.0",
|
||||
},
|
||||
"docker.io/redis:alpine": {
|
||||
registry: "docker.io",
|
||||
repo: "",
|
||||
image: "redis",
|
||||
tag: "alpine",
|
||||
},
|
||||
"nginx:alpine": {
|
||||
registry: "docker.io",
|
||||
repo: "",
|
||||
image: "nginx",
|
||||
tag: "alpine",
|
||||
},
|
||||
"nginx": {
|
||||
registry: "docker.io",
|
||||
repo: "",
|
||||
image: "nginx",
|
||||
tag: "latest",
|
||||
},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
r := NewRegistry(name)
|
||||
assert.Equal(t, tc.registry, r.Registry())
|
||||
assert.Equal(t, tc.repo, r.Repository())
|
||||
assert.Equal(t, tc.image, r.Image())
|
||||
assert.Equal(t, tc.tag, r.Tag())
|
||||
})
|
||||
}
|
||||
}
|
||||
11
api/v1alpha1/external_service_ips.go
Normal file
11
api/v1alpha1/external_service_ips.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// +kubebuilder:validation:Pattern="^([0-9]{1,3}.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
|
||||
type AllowedIP string
|
||||
|
||||
type ExternalServiceIPsSpec struct {
|
||||
Allowed []AllowedIP `json:"allowed"`
|
||||
}
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package v1alpha1 contains API Schema definitions for the capsule.clastix.io v1alpha1 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type IngressHostnamesList []string
|
||||
|
||||
func (hostnames IngressHostnamesList) Len() int {
|
||||
return len(hostnames)
|
||||
}
|
||||
|
||||
func (hostnames IngressHostnamesList) Swap(i, j int) {
|
||||
hostnames[i], hostnames[j] = hostnames[j], hostnames[i]
|
||||
}
|
||||
|
||||
func (hostnames IngressHostnamesList) Less(i, j int) bool {
|
||||
return hostnames[i] < hostnames[j]
|
||||
}
|
||||
|
||||
func (hostnames IngressHostnamesList) IsStringInList(value string) (ok bool) {
|
||||
sort.Sort(hostnames)
|
||||
i := sort.SearchStrings(hostnames, value)
|
||||
ok = i < hostnames.Len() && hostnames[i] == value
|
||||
return
|
||||
}
|
||||
17
api/v1alpha1/owner.go
Normal file
17
api/v1alpha1/owner.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// OwnerSpec defines tenant owner name and kind
|
||||
type OwnerSpec struct {
|
||||
Name string `json:"name"`
|
||||
Kind Kind `json:"kind"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=User;Group
|
||||
type Kind string
|
||||
|
||||
func (k Kind) String() string {
|
||||
return string(k)
|
||||
}
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -22,6 +9,13 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (t *Tenant) IsCordoned() bool {
|
||||
if v, ok := t.Labels["capsule.clastix.io/cordon"]; ok && v == "enabled" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tenant) IsFull() bool {
|
||||
// we don't have limits on assigned Namespaces
|
||||
if t.Spec.NamespaceQuota == nil {
|
||||
|
||||
@@ -1,18 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
|
||||
@@ -1,53 +1,22 @@
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type AdditionalMetadata struct {
|
||||
AdditionalLabels map[string]string `json:"additionalLabels,omitempty"`
|
||||
AdditionalAnnotations map[string]string `json:"additionalAnnotations,omitempty"`
|
||||
}
|
||||
|
||||
type IngressHostnamesSpec struct {
|
||||
Allowed IngressHostnamesList `json:"allowed"`
|
||||
AllowedRegex string `json:"allowedRegex"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Pattern="^([0-9]{1,3}.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
|
||||
type AllowedIP string
|
||||
|
||||
type ExternalServiceIPs struct {
|
||||
Allowed []AllowedIP `json:"allowed"`
|
||||
}
|
||||
|
||||
// TenantSpec defines the desired state of Tenant
|
||||
type TenantSpec struct {
|
||||
Owner OwnerSpec `json:"owner"`
|
||||
|
||||
//+kubebuilder:validation:Minimum=1
|
||||
NamespaceQuota *int32 `json:"namespaceQuota,omitempty"`
|
||||
NamespacesMetadata AdditionalMetadata `json:"namespacesMetadata,omitempty"`
|
||||
ServicesMetadata AdditionalMetadata `json:"servicesMetadata,omitempty"`
|
||||
NamespacesMetadata *AdditionalMetadataSpec `json:"namespacesMetadata,omitempty"`
|
||||
ServicesMetadata *AdditionalMetadataSpec `json:"servicesMetadata,omitempty"`
|
||||
StorageClasses *AllowedListSpec `json:"storageClasses,omitempty"`
|
||||
IngressClasses *AllowedListSpec `json:"ingressClasses,omitempty"`
|
||||
IngressHostnames *AllowedListSpec `json:"ingressHostnames,omitempty"`
|
||||
@@ -56,27 +25,8 @@ type TenantSpec struct {
|
||||
NetworkPolicies []networkingv1.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
LimitRanges []corev1.LimitRangeSpec `json:"limitRanges,omitempty"`
|
||||
ResourceQuota []corev1.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
AdditionalRoleBindings []AdditionalRoleBindings `json:"additionalRoleBindings,omitempty"`
|
||||
ExternalServiceIPs *ExternalServiceIPs `json:"externalServiceIPs,omitempty"`
|
||||
}
|
||||
|
||||
type AdditionalRoleBindings struct {
|
||||
ClusterRoleName string `json:"clusterRoleName"`
|
||||
// kubebuilder:validation:Minimum=1
|
||||
Subjects []rbacv1.Subject `json:"subjects"`
|
||||
}
|
||||
|
||||
// OwnerSpec defines tenant owner name and kind
|
||||
type OwnerSpec struct {
|
||||
Name string `json:"name"`
|
||||
Kind Kind `json:"kind"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=User;Group
|
||||
type Kind string
|
||||
|
||||
func (k Kind) String() string {
|
||||
return string(k)
|
||||
AdditionalRoleBindings []AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
ExternalServiceIPs *ExternalServiceIPsSpec `json:"externalServiceIPs,omitempty"`
|
||||
}
|
||||
|
||||
// TenantStatus defines the observed state of Tenant
|
||||
|
||||
21
api/v1alpha1/tenant_webhook.go
Normal file
21
api/v1alpha1/tenant_webhook.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
func (t *Tenant) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
||||
certData, _ := ioutil.ReadFile("/tmp/k8s-webhook-server/serving-certs/tls.crt")
|
||||
if len(certData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ctrl.NewWebhookManagedBy(mgr).
|
||||
For(t).
|
||||
Complete()
|
||||
}
|
||||
@@ -1,20 +1,7 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright 2020 Clastix Labs.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
@@ -22,13 +9,13 @@ package v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AdditionalMetadata) DeepCopyInto(out *AdditionalMetadata) {
|
||||
func (in *AdditionalMetadataSpec) DeepCopyInto(out *AdditionalMetadataSpec) {
|
||||
*out = *in
|
||||
if in.AdditionalLabels != nil {
|
||||
in, out := &in.AdditionalLabels, &out.AdditionalLabels
|
||||
@@ -46,32 +33,32 @@ func (in *AdditionalMetadata) DeepCopyInto(out *AdditionalMetadata) {
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalMetadata.
|
||||
func (in *AdditionalMetadata) DeepCopy() *AdditionalMetadata {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalMetadataSpec.
|
||||
func (in *AdditionalMetadataSpec) DeepCopy() *AdditionalMetadataSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AdditionalMetadata)
|
||||
out := new(AdditionalMetadataSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AdditionalRoleBindings) DeepCopyInto(out *AdditionalRoleBindings) {
|
||||
func (in *AdditionalRoleBindingsSpec) DeepCopyInto(out *AdditionalRoleBindingsSpec) {
|
||||
*out = *in
|
||||
if in.Subjects != nil {
|
||||
in, out := &in.Subjects, &out.Subjects
|
||||
*out = make([]rbacv1.Subject, len(*in))
|
||||
*out = make([]v1.Subject, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalRoleBindings.
|
||||
func (in *AdditionalRoleBindings) DeepCopy() *AdditionalRoleBindings {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalRoleBindingsSpec.
|
||||
func (in *AdditionalRoleBindingsSpec) DeepCopy() *AdditionalRoleBindingsSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AdditionalRoleBindings)
|
||||
out := new(AdditionalRoleBindingsSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -97,7 +84,85 @@ func (in *AllowedListSpec) DeepCopy() *AllowedListSpec {
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ExternalServiceIPs) DeepCopyInto(out *ExternalServiceIPs) {
|
||||
func (in *CapsuleConfiguration) DeepCopyInto(out *CapsuleConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfiguration.
|
||||
func (in *CapsuleConfiguration) DeepCopy() *CapsuleConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CapsuleConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *CapsuleConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CapsuleConfigurationList) DeepCopyInto(out *CapsuleConfigurationList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]CapsuleConfiguration, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfigurationList.
|
||||
func (in *CapsuleConfigurationList) DeepCopy() *CapsuleConfigurationList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CapsuleConfigurationList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *CapsuleConfigurationList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CapsuleConfigurationSpec) DeepCopyInto(out *CapsuleConfigurationSpec) {
|
||||
*out = *in
|
||||
if in.UserGroups != nil {
|
||||
in, out := &in.UserGroups, &out.UserGroups
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CapsuleConfigurationSpec.
|
||||
func (in *CapsuleConfigurationSpec) DeepCopy() *CapsuleConfigurationSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CapsuleConfigurationSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ExternalServiceIPsSpec) DeepCopyInto(out *ExternalServiceIPsSpec) {
|
||||
*out = *in
|
||||
if in.Allowed != nil {
|
||||
in, out := &in.Allowed, &out.Allowed
|
||||
@@ -106,51 +171,12 @@ func (in *ExternalServiceIPs) DeepCopyInto(out *ExternalServiceIPs) {
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalServiceIPs.
|
||||
func (in *ExternalServiceIPs) DeepCopy() *ExternalServiceIPs {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalServiceIPsSpec.
|
||||
func (in *ExternalServiceIPsSpec) DeepCopy() *ExternalServiceIPsSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ExternalServiceIPs)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in IngressHostnamesList) DeepCopyInto(out *IngressHostnamesList) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(IngressHostnamesList, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressHostnamesList.
|
||||
func (in IngressHostnamesList) DeepCopy() IngressHostnamesList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(IngressHostnamesList)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IngressHostnamesSpec) DeepCopyInto(out *IngressHostnamesSpec) {
|
||||
*out = *in
|
||||
if in.Allowed != nil {
|
||||
in, out := &in.Allowed, &out.Allowed
|
||||
*out = make(IngressHostnamesList, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressHostnamesSpec.
|
||||
func (in *IngressHostnamesSpec) DeepCopy() *IngressHostnamesSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(IngressHostnamesSpec)
|
||||
out := new(ExternalServiceIPsSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -238,8 +264,16 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
in.NamespacesMetadata.DeepCopyInto(&out.NamespacesMetadata)
|
||||
in.ServicesMetadata.DeepCopyInto(&out.ServicesMetadata)
|
||||
if in.NamespacesMetadata != nil {
|
||||
in, out := &in.NamespacesMetadata, &out.NamespacesMetadata
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ServicesMetadata != nil {
|
||||
in, out := &in.ServicesMetadata, &out.ServicesMetadata
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StorageClasses != nil {
|
||||
in, out := &in.StorageClasses, &out.StorageClasses
|
||||
*out = new(AllowedListSpec)
|
||||
@@ -269,7 +303,7 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
}
|
||||
if in.NetworkPolicies != nil {
|
||||
in, out := &in.NetworkPolicies, &out.NetworkPolicies
|
||||
*out = make([]v1.NetworkPolicySpec, len(*in))
|
||||
*out = make([]networkingv1.NetworkPolicySpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
@@ -290,14 +324,14 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
}
|
||||
if in.AdditionalRoleBindings != nil {
|
||||
in, out := &in.AdditionalRoleBindings, &out.AdditionalRoleBindings
|
||||
*out = make([]AdditionalRoleBindings, len(*in))
|
||||
*out = make([]AdditionalRoleBindingsSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.ExternalServiceIPs != nil {
|
||||
in, out := &in.ExternalServiceIPs, &out.ExternalServiceIPs
|
||||
*out = new(ExternalServiceIPs)
|
||||
*out = new(ExternalServiceIPsSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
9
api/v1beta1/additional_metadata.go
Normal file
9
api/v1beta1/additional_metadata.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
type AdditionalMetadataSpec struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
12
api/v1beta1/additional_role_bindings.go
Normal file
12
api/v1beta1/additional_role_bindings.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import rbacv1 "k8s.io/api/rbac/v1"
|
||||
|
||||
type AdditionalRoleBindingsSpec struct {
|
||||
ClusterRoleName string `json:"clusterRoleName"`
|
||||
// kubebuilder:validation:Minimum=1
|
||||
Subjects []rbacv1.Subject `json:"subjects"`
|
||||
}
|
||||
33
api/v1beta1/allowed_list.go
Normal file
33
api/v1beta1/allowed_list.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//nolint:dupl
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type AllowedListSpec struct {
|
||||
Exact []string `json:"allowed,omitempty"`
|
||||
Regex string `json:"allowedRegex,omitempty"`
|
||||
}
|
||||
|
||||
func (in *AllowedListSpec) ExactMatch(value string) (ok bool) {
|
||||
if len(in.Exact) > 0 {
|
||||
sort.SliceStable(in.Exact, func(i, j int) bool {
|
||||
return strings.ToLower(in.Exact[i]) < strings.ToLower(in.Exact[j])
|
||||
})
|
||||
i := sort.SearchStrings(in.Exact, value)
|
||||
ok = i < len(in.Exact) && in.Exact[i] == value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (in AllowedListSpec) RegexMatch(value string) (ok bool) {
|
||||
if len(in.Regex) > 0 {
|
||||
ok = regexp.MustCompile(in.Regex).MatchString(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
67
api/v1beta1/allowed_list_test.go
Normal file
67
api/v1beta1/allowed_list_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//nolint:dupl
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAllowedListSpec_ExactMatch(t *testing.T) {
|
||||
type tc struct {
|
||||
In []string
|
||||
True []string
|
||||
False []string
|
||||
}
|
||||
for _, tc := range []tc{
|
||||
{
|
||||
[]string{"foo", "bar", "bizz", "buzz"},
|
||||
[]string{"foo", "bar", "bizz", "buzz"},
|
||||
[]string{"bing", "bong"},
|
||||
},
|
||||
{
|
||||
[]string{"one", "two", "three"},
|
||||
[]string{"one", "two", "three"},
|
||||
[]string{"a", "b", "c"},
|
||||
},
|
||||
{
|
||||
nil,
|
||||
nil,
|
||||
[]string{"any", "value"},
|
||||
},
|
||||
} {
|
||||
a := AllowedListSpec{
|
||||
Exact: tc.In,
|
||||
}
|
||||
for _, ok := range tc.True {
|
||||
assert.True(t, a.ExactMatch(ok))
|
||||
}
|
||||
for _, ko := range tc.False {
|
||||
assert.False(t, a.ExactMatch(ko))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllowedListSpec_RegexMatch(t *testing.T) {
|
||||
type tc struct {
|
||||
Regex string
|
||||
True []string
|
||||
False []string
|
||||
}
|
||||
for _, tc := range []tc{
|
||||
{`first-\w+-pattern`, []string{"first-date-pattern", "first-year-pattern"}, []string{"broken", "first-year", "second-date-pattern"}},
|
||||
{``, nil, []string{"any", "value"}},
|
||||
} {
|
||||
a := AllowedListSpec{
|
||||
Regex: tc.Regex,
|
||||
}
|
||||
for _, ok := range tc.True {
|
||||
assert.True(t, a.RegexMatch(ok))
|
||||
}
|
||||
for _, ko := range tc.False {
|
||||
assert.False(t, a.RegexMatch(ko))
|
||||
}
|
||||
}
|
||||
}
|
||||
15
api/v1beta1/deny_wildcard.go
Normal file
15
api/v1beta1/deny_wildcard.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
const (
|
||||
denyWildcard = "capsule.clastix.io/deny-wildcard"
|
||||
)
|
||||
|
||||
func (t *Tenant) IsWildcardDenied() bool {
|
||||
if v, ok := t.Annotations[denyWildcard]; ok && v == "true" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
33
api/v1beta1/forbidden_list.go
Normal file
33
api/v1beta1/forbidden_list.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//nolint:dupl
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ForbiddenListSpec struct {
|
||||
Exact []string `json:"denied,omitempty"`
|
||||
Regex string `json:"deniedRegex,omitempty"`
|
||||
}
|
||||
|
||||
func (in *ForbiddenListSpec) ExactMatch(value string) (ok bool) {
|
||||
if len(in.Exact) > 0 {
|
||||
sort.SliceStable(in.Exact, func(i, j int) bool {
|
||||
return strings.ToLower(in.Exact[i]) < strings.ToLower(in.Exact[j])
|
||||
})
|
||||
i := sort.SearchStrings(in.Exact, value)
|
||||
ok = i < len(in.Exact) && in.Exact[i] == value
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (in ForbiddenListSpec) RegexMatch(value string) (ok bool) {
|
||||
if len(in.Regex) > 0 {
|
||||
ok = regexp.MustCompile(in.Regex).MatchString(value)
|
||||
}
|
||||
return
|
||||
}
|
||||
67
api/v1beta1/forbidden_list_test.go
Normal file
67
api/v1beta1/forbidden_list_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//nolint:dupl
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestForbiddenListSpec_ExactMatch(t *testing.T) {
|
||||
type tc struct {
|
||||
In []string
|
||||
True []string
|
||||
False []string
|
||||
}
|
||||
for _, tc := range []tc{
|
||||
{
|
||||
[]string{"foo", "bar", "bizz", "buzz"},
|
||||
[]string{"foo", "bar", "bizz", "buzz"},
|
||||
[]string{"bing", "bong"},
|
||||
},
|
||||
{
|
||||
[]string{"one", "two", "three"},
|
||||
[]string{"one", "two", "three"},
|
||||
[]string{"a", "b", "c"},
|
||||
},
|
||||
{
|
||||
nil,
|
||||
nil,
|
||||
[]string{"any", "value"},
|
||||
},
|
||||
} {
|
||||
a := ForbiddenListSpec{
|
||||
Exact: tc.In,
|
||||
}
|
||||
for _, ok := range tc.True {
|
||||
assert.True(t, a.ExactMatch(ok))
|
||||
}
|
||||
for _, ko := range tc.False {
|
||||
assert.False(t, a.ExactMatch(ko))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestForbiddenListSpec_RegexMatch(t *testing.T) {
|
||||
type tc struct {
|
||||
Regex string
|
||||
True []string
|
||||
False []string
|
||||
}
|
||||
for _, tc := range []tc{
|
||||
{`first-\w+-pattern`, []string{"first-date-pattern", "first-year-pattern"}, []string{"broken", "first-year", "second-date-pattern"}},
|
||||
{``, nil, []string{"any", "value"}},
|
||||
} {
|
||||
a := ForbiddenListSpec{
|
||||
Regex: tc.Regex,
|
||||
}
|
||||
for _, ok := range tc.True {
|
||||
assert.True(t, a.RegexMatch(ok))
|
||||
}
|
||||
for _, ko := range tc.False {
|
||||
assert.False(t, a.RegexMatch(ko))
|
||||
}
|
||||
}
|
||||
}
|
||||
23
api/v1beta1/groupversion_info.go
Normal file
23
api/v1beta1/groupversion_info.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package v1beta1 contains API Schema definitions for the capsule v1beta1 API group
|
||||
//+kubebuilder:object:generate=true
|
||||
//+groupName=capsule.clastix.io
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is group version used to register these objects
|
||||
GroupVersion = schema.GroupVersion{Group: "capsule.clastix.io", Version: "v1beta1"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
14
api/v1beta1/hostname_collision_scope.go
Normal file
14
api/v1beta1/hostname_collision_scope.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
const (
|
||||
HostnameCollisionScopeCluster HostnameCollisionScope = "Cluster"
|
||||
HostnameCollisionScopeTenant HostnameCollisionScope = "Tenant"
|
||||
HostnameCollisionScopeNamespace HostnameCollisionScope = "Namespace"
|
||||
HostnameCollisionScopeDisabled HostnameCollisionScope = "Disabled"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:Enum=Cluster;Tenant;Namespace;Disabled
|
||||
type HostnameCollisionScope string
|
||||
11
api/v1beta1/image_pull_policy.go
Normal file
11
api/v1beta1/image_pull_policy.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
// +kubebuilder:validation:Enum=Always;Never;IfNotPresent
|
||||
type ImagePullPolicySpec string
|
||||
|
||||
func (i ImagePullPolicySpec) String() string {
|
||||
return string(i)
|
||||
}
|
||||
24
api/v1beta1/ingress_options.go
Normal file
24
api/v1beta1/ingress_options.go
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
type IngressOptions struct {
|
||||
// Specifies the allowed IngressClasses assigned to the Tenant. Capsule assures that all Ingress resources created in the Tenant can use only one of the allowed IngressClasses. Optional.
|
||||
AllowedClasses *AllowedListSpec `json:"allowedClasses,omitempty"`
|
||||
// Defines the scope of hostname collision check performed when Tenant Owners create Ingress with allowed hostnames.
|
||||
//
|
||||
//
|
||||
// - Cluster: disallow the creation of an Ingress if the pair hostname and path is already used across the Namespaces managed by Capsule.
|
||||
//
|
||||
// - Tenant: disallow the creation of an Ingress if the pair hostname and path is already used across the Namespaces of the Tenant.
|
||||
//
|
||||
// - Namespace: disallow the creation of an Ingress if the pair hostname and path is already used in the Ingress Namespace.
|
||||
//
|
||||
//
|
||||
// Optional.
|
||||
// +kubebuilder:default=Disabled
|
||||
HostnameCollisionScope HostnameCollisionScope `json:"hostnameCollisionScope,omitempty"`
|
||||
// Specifies the allowed hostnames in Ingresses for the given Tenant. Capsule assures that all Ingress resources created in the Tenant can use only one of the allowed hostnames. Optional.
|
||||
AllowedHostnames *AllowedListSpec `json:"allowedHostnames,omitempty"`
|
||||
}
|
||||
10
api/v1beta1/limit_ranges.go
Normal file
10
api/v1beta1/limit_ranges.go
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
|
||||
type LimitRangesSpec struct {
|
||||
Items []corev1.LimitRangeSpec `json:"items,omitempty"`
|
||||
}
|
||||
51
api/v1beta1/namespace_options.go
Normal file
51
api/v1beta1/namespace_options.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package v1beta1
|
||||
|
||||
import "strings"
|
||||
|
||||
type NamespaceOptions struct {
|
||||
//+kubebuilder:validation:Minimum=1
|
||||
// Specifies the maximum number of namespaces allowed for that Tenant. Once the namespace quota assigned to the Tenant has been reached, the Tenant owner cannot create further namespaces. Optional.
|
||||
Quota *int32 `json:"quota,omitempty"`
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Namespace resource in the Tenant. Optional.
|
||||
AdditionalMetadata *AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceLabelsAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tenant) hasForbiddenNamespaceAnnotationsAnnotations() bool {
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceAnnotationsAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tenant) ForbiddenUserNamespaceLabels() *ForbiddenListSpec {
|
||||
if !t.hasForbiddenNamespaceLabelsAnnotations() {
|
||||
return nil
|
||||
}
|
||||
return &ForbiddenListSpec{
|
||||
Exact: strings.Split(t.Annotations[ForbiddenNamespaceLabelsAnnotation], ","),
|
||||
Regex: t.Annotations[ForbiddenNamespaceLabelsRegexpAnnotation],
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tenant) ForbiddenUserNamespaceAnnotations() *ForbiddenListSpec {
|
||||
if !t.hasForbiddenNamespaceAnnotationsAnnotations() {
|
||||
return nil
|
||||
}
|
||||
return &ForbiddenListSpec{
|
||||
Exact: strings.Split(t.Annotations[ForbiddenNamespaceAnnotationsAnnotation], ","),
|
||||
Regex: t.Annotations[ForbiddenNamespaceAnnotationsRegexpAnnotation],
|
||||
}
|
||||
}
|
||||
12
api/v1beta1/network_policy.go
Normal file
12
api/v1beta1/network_policy.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
)
|
||||
|
||||
type NetworkPolicySpec struct {
|
||||
Items []networkingv1.NetworkPolicySpec `json:"items,omitempty"`
|
||||
}
|
||||
54
api/v1beta1/owner.go
Normal file
54
api/v1beta1/owner.go
Normal file
@@ -0,0 +1,54 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
type OwnerSpec struct {
|
||||
// Kind of tenant owner. Possible values are "User", "Group", and "ServiceAccount"
|
||||
Kind OwnerKind `json:"kind"`
|
||||
// Name of tenant owner.
|
||||
Name string `json:"name"`
|
||||
// Proxy settings for tenant owner.
|
||||
ProxyOperations []ProxySettings `json:"proxySettings,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=User;Group;ServiceAccount
|
||||
type OwnerKind string
|
||||
|
||||
func (k OwnerKind) String() string {
|
||||
return string(k)
|
||||
}
|
||||
|
||||
type ProxySettings struct {
|
||||
Kind ProxyServiceKind `json:"kind"`
|
||||
Operations []ProxyOperation `json:"operations"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=List;Update;Delete
|
||||
type ProxyOperation string
|
||||
|
||||
func (p ProxyOperation) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=Nodes;StorageClasses;IngressClasses;PriorityClasses
|
||||
type ProxyServiceKind string
|
||||
|
||||
func (p ProxyServiceKind) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
const (
|
||||
NodesProxy ProxyServiceKind = "Nodes"
|
||||
StorageClassesProxy ProxyServiceKind = "StorageClasses"
|
||||
IngressClassesProxy ProxyServiceKind = "IngressClasses"
|
||||
PriorityClassesProxy ProxyServiceKind = "PriorityClasses"
|
||||
|
||||
ListOperation ProxyOperation = "List"
|
||||
UpdateOperation ProxyOperation = "Update"
|
||||
DeleteOperation ProxyOperation = "Delete"
|
||||
|
||||
UserOwner OwnerKind = "User"
|
||||
GroupOwner OwnerKind = "Group"
|
||||
ServiceAccountOwner OwnerKind = "ServiceAccount"
|
||||
)
|
||||
34
api/v1beta1/owner_list.go
Normal file
34
api/v1beta1/owner_list.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
type OwnerListSpec []OwnerSpec
|
||||
|
||||
func (o OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec) {
|
||||
sort.Sort(ByKindAndName(o))
|
||||
i := sort.Search(len(o), func(i int) bool {
|
||||
return o[i].Kind >= kind && o[i].Name >= name
|
||||
})
|
||||
|
||||
if i < len(o) && o[i].Kind == kind && o[i].Name == name {
|
||||
return o[i]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ByKindAndName OwnerListSpec
|
||||
|
||||
func (b ByKindAndName) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
func (b ByKindAndName) Less(i, j int) bool {
|
||||
if b[i].Kind.String() != b[j].Kind.String() {
|
||||
return b[i].Kind.String() < b[j].Kind.String()
|
||||
}
|
||||
return b[i].Name < b[j].Name
|
||||
}
|
||||
func (b ByKindAndName) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
83
api/v1beta1/owner_list_test.go
Normal file
83
api/v1beta1/owner_list_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOwnerListSpec_FindOwner(t *testing.T) {
|
||||
var bla = OwnerSpec{
|
||||
Kind: UserOwner,
|
||||
Name: "bla",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: IngressClassesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var bar = OwnerSpec{
|
||||
Kind: GroupOwner,
|
||||
Name: "bar",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var baz = OwnerSpec{
|
||||
Kind: UserOwner,
|
||||
Name: "baz",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"Update"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var fim = OwnerSpec{
|
||||
Kind: ServiceAccountOwner,
|
||||
Name: "fim",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: NodesProxy,
|
||||
Operations: []ProxyOperation{"List"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var bom = OwnerSpec{
|
||||
Kind: GroupOwner,
|
||||
Name: "bom",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
{
|
||||
Kind: NodesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var qip = OwnerSpec{
|
||||
Kind: ServiceAccountOwner,
|
||||
Name: "qip",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"List", "Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
var owners = OwnerListSpec{bom, qip, bla, bar, baz, fim}
|
||||
|
||||
assert.Equal(t, owners.FindOwner("bom", GroupOwner), bom)
|
||||
assert.Equal(t, owners.FindOwner("qip", ServiceAccountOwner), qip)
|
||||
assert.Equal(t, owners.FindOwner("bla", UserOwner), bla)
|
||||
assert.Equal(t, owners.FindOwner("bar", GroupOwner), bar)
|
||||
assert.Equal(t, owners.FindOwner("baz", UserOwner), baz)
|
||||
assert.Equal(t, owners.FindOwner("fim", ServiceAccountOwner), fim)
|
||||
assert.Equal(t, owners.FindOwner("notfound", ServiceAccountOwner), OwnerSpec{})
|
||||
}
|
||||
21
api/v1beta1/resource_quota.go
Normal file
21
api/v1beta1/resource_quota.go
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
|
||||
// +kubebuilder:validation:Enum=Tenant;Namespace
|
||||
type ResourceQuotaScope string
|
||||
|
||||
const (
|
||||
ResourceQuotaScopeTenant ResourceQuotaScope = "Tenant"
|
||||
ResourceQuotaScopeNamespace ResourceQuotaScope = "Namespace"
|
||||
)
|
||||
|
||||
type ResourceQuotaSpec struct {
|
||||
// +kubebuilder:default=Tenant
|
||||
// Define if the Resource Budget should compute resource across all Namespaces in the Tenant or individually per cluster. Default is Tenant
|
||||
Scope ResourceQuotaScope `json:"scope,omitempty"`
|
||||
Items []corev1.ResourceQuotaSpec `json:"items,omitempty"`
|
||||
}
|
||||
11
api/v1beta1/service_allowed_ips.go
Normal file
11
api/v1beta1/service_allowed_ips.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
// +kubebuilder:validation:Pattern="^([0-9]{1,3}.){3}[0-9]{1,3}(/([0-9]|[1-2][0-9]|3[0-2]))?$"
|
||||
type AllowedIP string
|
||||
|
||||
type ExternalServiceIPsSpec struct {
|
||||
Allowed []AllowedIP `json:"allowed"`
|
||||
}
|
||||
16
api/v1beta1/service_allowed_types.go
Normal file
16
api/v1beta1/service_allowed_types.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
type AllowedServices struct {
|
||||
//+kubebuilder:default=true
|
||||
// Specifies if NodePort service type resources are allowed for the Tenant. Default is true. Optional.
|
||||
NodePort *bool `json:"nodePort,omitempty"`
|
||||
//+kubebuilder:default=true
|
||||
// Specifies if ExternalName service type resources are allowed for the Tenant. Default is true. Optional.
|
||||
ExternalName *bool `json:"externalName,omitempty"`
|
||||
//+kubebuilder:default=true
|
||||
// Specifies if LoadBalancer service type resources are allowed for the Tenant. Default is true. Optional.
|
||||
LoadBalancer *bool `json:"loadBalancer,omitempty"`
|
||||
}
|
||||
13
api/v1beta1/service_options.go
Normal file
13
api/v1beta1/service_options.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
type ServiceOptions struct {
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Service resource in the Tenant. Optional.
|
||||
AdditionalMetadata *AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
// Block or deny certain type of Services. Optional.
|
||||
AllowedServices *AllowedServices `json:"allowedServices,omitempty"`
|
||||
// Specifies the external IPs that can be used in Services with type ClusterIP. An empty list means no IPs are allowed. Optional.
|
||||
ExternalServiceIPs *ExternalServiceIPsSpec `json:"externalIPs,omitempty"`
|
||||
}
|
||||
29
api/v1beta1/tenant_annotations.go
Normal file
29
api/v1beta1/tenant_annotations.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
AvailableIngressClassesAnnotation = "capsule.clastix.io/ingress-classes"
|
||||
AvailableIngressClassesRegexpAnnotation = "capsule.clastix.io/ingress-classes-regexp"
|
||||
AvailableStorageClassesAnnotation = "capsule.clastix.io/storage-classes"
|
||||
AvailableStorageClassesRegexpAnnotation = "capsule.clastix.io/storage-classes-regexp"
|
||||
AllowedRegistriesAnnotation = "capsule.clastix.io/allowed-registries"
|
||||
AllowedRegistriesRegexpAnnotation = "capsule.clastix.io/allowed-registries-regexp"
|
||||
ForbiddenNamespaceLabelsAnnotation = "capsule.clastix.io/forbidden-namespace-labels"
|
||||
ForbiddenNamespaceLabelsRegexpAnnotation = "capsule.clastix.io/forbidden-namespace-labels-regexp"
|
||||
ForbiddenNamespaceAnnotationsAnnotation = "capsule.clastix.io/forbidden-namespace-annotations"
|
||||
ForbiddenNamespaceAnnotationsRegexpAnnotation = "capsule.clastix.io/forbidden-namespace-annotations-regexp"
|
||||
)
|
||||
|
||||
func UsedQuotaFor(resource fmt.Stringer) string {
|
||||
return "quota.capsule.clastix.io/used-" + resource.String()
|
||||
}
|
||||
|
||||
func HardQuotaFor(resource fmt.Stringer) string {
|
||||
return "quota.capsule.clastix.io/hard-" + resource.String()
|
||||
}
|
||||
42
api/v1beta1/tenant_func.go
Normal file
42
api/v1beta1/tenant_func.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (t *Tenant) IsCordoned() bool {
|
||||
if v, ok := t.Labels["capsule.clastix.io/cordon"]; ok && v == "enabled" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Tenant) IsFull() bool {
|
||||
// we don't have limits on assigned Namespaces
|
||||
if t.Spec.NamespaceOptions == nil || t.Spec.NamespaceOptions.Quota == nil {
|
||||
return false
|
||||
}
|
||||
return len(t.Status.Namespaces) >= int(*t.Spec.NamespaceOptions.Quota)
|
||||
}
|
||||
|
||||
func (t *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
var l []string
|
||||
for _, ns := range namespaces {
|
||||
if ns.Status.Phase == corev1.NamespaceActive {
|
||||
l = append(l, ns.GetName())
|
||||
}
|
||||
}
|
||||
sort.Strings(l)
|
||||
|
||||
t.Status.Namespaces = l
|
||||
t.Status.Size = uint(len(l))
|
||||
}
|
||||
|
||||
func (t *Tenant) GetOwnerProxySettings(name string, kind OwnerKind) []ProxySettings {
|
||||
return t.Spec.Owners.FindOwner(name, kind).ProxyOperations
|
||||
}
|
||||
31
api/v1beta1/tenant_labels.go
Normal file
31
api/v1beta1/tenant_labels.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func GetTypeLabel(t runtime.Object) (label string, err error) {
|
||||
switch v := t.(type) {
|
||||
case *Tenant:
|
||||
return "capsule.clastix.io/tenant", nil
|
||||
case *corev1.LimitRange:
|
||||
return "capsule.clastix.io/limit-range", nil
|
||||
case *networkingv1.NetworkPolicy:
|
||||
return "capsule.clastix.io/network-policy", nil
|
||||
case *corev1.ResourceQuota:
|
||||
return "capsule.clastix.io/resource-quota", nil
|
||||
case *rbacv1.RoleBinding:
|
||||
return "capsule.clastix.io/role-binding", nil
|
||||
default:
|
||||
err = fmt.Errorf("type %T is not mapped as Capsule label recognized", v)
|
||||
}
|
||||
return
|
||||
}
|
||||
23
api/v1beta1/tenant_status.go
Normal file
23
api/v1beta1/tenant_status.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
// +kubebuilder:validation:Enum=Cordoned;Active
|
||||
type tenantState string
|
||||
|
||||
const (
|
||||
TenantStateActive tenantState = "Active"
|
||||
TenantStateCordoned tenantState = "Cordoned"
|
||||
)
|
||||
|
||||
// Returns the observed state of the Tenant
|
||||
type TenantStatus struct {
|
||||
//+kubebuilder:default=Active
|
||||
// The operational state of the Tenant. Possible values are "Active", "Cordoned".
|
||||
State tenantState `json:"state"`
|
||||
// How many namespaces are assigned to the Tenant.
|
||||
Size uint `json:"size"`
|
||||
// List of namespaces assigned to the Tenant.
|
||||
Namespaces []string `json:"namespaces,omitempty"`
|
||||
}
|
||||
72
api/v1beta1/tenant_types.go
Normal file
72
api/v1beta1/tenant_types.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// TenantSpec defines the desired state of Tenant
|
||||
type TenantSpec struct {
|
||||
// Specifies the owners of the Tenant. Mandatory.
|
||||
Owners OwnerListSpec `json:"owners"`
|
||||
// Specifies options for the Namespaces, such as additional metadata or maximum number of namespaces allowed for that Tenant. Once the namespace quota assigned to the Tenant has been reached, the Tenant owner cannot create further namespaces. Optional.
|
||||
NamespaceOptions *NamespaceOptions `json:"namespaceOptions,omitempty"`
|
||||
// Specifies options for the Service, such as additional metadata or block of certain type of Services. Optional.
|
||||
ServiceOptions *ServiceOptions `json:"serviceOptions,omitempty"`
|
||||
// Specifies the allowed StorageClasses assigned to the Tenant. Capsule assures that all PersistentVolumeClaim resources created in the Tenant can use only one of the allowed StorageClasses. Optional.
|
||||
StorageClasses *AllowedListSpec `json:"storageClasses,omitempty"`
|
||||
// Specifies options for the Ingress resources, such as allowed hostnames and IngressClass. Optional.
|
||||
IngressOptions IngressOptions `json:"ingressOptions,omitempty"`
|
||||
// Specifies the trusted Image Registries assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed trusted registries. Optional.
|
||||
ContainerRegistries *AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// Specifies the label to control the placement of pods on a given pool of worker nodes. All namesapces created within the Tenant will have the node selector annotation. This annotation tells the Kubernetes scheduler to place pods on the nodes having the selector label. Optional.
|
||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
NetworkPolicies NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
// Specifies the NetworkPolicies assigned to the Tenant. The assigned NetworkPolicies are inherited by any namespace created in the Tenant. Optional.
|
||||
LimitRanges LimitRangesSpec `json:"limitRanges,omitempty"`
|
||||
// Specifies a list of ResourceQuota resources assigned to the Tenant. The assigned values are inherited by any namespace created in the Tenant. The Capsule operator aggregates ResourceQuota at Tenant level, so that the hard quota is never crossed for the given Tenant. This permits the Tenant owner to consume resources in the Tenant regardless of the namespace. Optional.
|
||||
ResourceQuota ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
// Specifies additional RoleBindings assigned to the Tenant. Capsule will ensure that all namespaces in the Tenant always contain the RoleBinding for the given ClusterRole. Optional.
|
||||
AdditionalRoleBindings []AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
// Specify the allowed values for the imagePullPolicies option in Pod resources. Capsule assures that all Pod resources created in the Tenant can use only one of the allowed policy. Optional.
|
||||
ImagePullPolicies []ImagePullPolicySpec `json:"imagePullPolicies,omitempty"`
|
||||
// Specifies the allowed priorityClasses assigned to the Tenant. Capsule assures that all Pods resources created in the Tenant can use only one of the allowed PriorityClasses. Optional.
|
||||
PriorityClasses *AllowedListSpec `json:"priorityClasses,omitempty"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
//+kubebuilder:subresource:status
|
||||
//+kubebuilder:storageversion
|
||||
// +kubebuilder:resource:scope=Cluster,shortName=tnt
|
||||
// +kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state",description="The actual state of the Tenant"
|
||||
// +kubebuilder:printcolumn:name="Namespace quota",type="integer",JSONPath=".spec.namespaceOptions.quota",description="The max amount of Namespaces can be created"
|
||||
// +kubebuilder:printcolumn:name="Namespace count",type="integer",JSONPath=".status.size",description="The total amount of Namespaces in use"
|
||||
// +kubebuilder:printcolumn:name="Node selector",type="string",JSONPath=".spec.nodeSelector",description="Node Selector applied to Pods"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
|
||||
|
||||
// Tenant is the Schema for the tenants API
|
||||
type Tenant struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec TenantSpec `json:"spec,omitempty"`
|
||||
Status TenantStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (t *Tenant) Hub() {}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// TenantList contains a list of Tenant
|
||||
type TenantList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []Tenant `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Tenant{}, &TenantList{})
|
||||
}
|
||||
533
api/v1beta1/zz_generated.deepcopy.go
Normal file
533
api/v1beta1/zz_generated.deepcopy.go
Normal file
@@ -0,0 +1,533 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AdditionalMetadataSpec) DeepCopyInto(out *AdditionalMetadataSpec) {
|
||||
*out = *in
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalMetadataSpec.
|
||||
func (in *AdditionalMetadataSpec) DeepCopy() *AdditionalMetadataSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AdditionalMetadataSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AdditionalRoleBindingsSpec) DeepCopyInto(out *AdditionalRoleBindingsSpec) {
|
||||
*out = *in
|
||||
if in.Subjects != nil {
|
||||
in, out := &in.Subjects, &out.Subjects
|
||||
*out = make([]v1.Subject, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AdditionalRoleBindingsSpec.
|
||||
func (in *AdditionalRoleBindingsSpec) DeepCopy() *AdditionalRoleBindingsSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AdditionalRoleBindingsSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AllowedListSpec) DeepCopyInto(out *AllowedListSpec) {
|
||||
*out = *in
|
||||
if in.Exact != nil {
|
||||
in, out := &in.Exact, &out.Exact
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedListSpec.
|
||||
func (in *AllowedListSpec) DeepCopy() *AllowedListSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AllowedListSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AllowedServices) DeepCopyInto(out *AllowedServices) {
|
||||
*out = *in
|
||||
if in.NodePort != nil {
|
||||
in, out := &in.NodePort, &out.NodePort
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.ExternalName != nil {
|
||||
in, out := &in.ExternalName, &out.ExternalName
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.LoadBalancer != nil {
|
||||
in, out := &in.LoadBalancer, &out.LoadBalancer
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedServices.
|
||||
func (in *AllowedServices) DeepCopy() *AllowedServices {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AllowedServices)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in ByKindAndName) DeepCopyInto(out *ByKindAndName) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(ByKindAndName, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ByKindAndName.
|
||||
func (in ByKindAndName) DeepCopy() ByKindAndName {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ByKindAndName)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ExternalServiceIPsSpec) DeepCopyInto(out *ExternalServiceIPsSpec) {
|
||||
*out = *in
|
||||
if in.Allowed != nil {
|
||||
in, out := &in.Allowed, &out.Allowed
|
||||
*out = make([]AllowedIP, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExternalServiceIPsSpec.
|
||||
func (in *ExternalServiceIPsSpec) DeepCopy() *ExternalServiceIPsSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ExternalServiceIPsSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ForbiddenListSpec) DeepCopyInto(out *ForbiddenListSpec) {
|
||||
*out = *in
|
||||
if in.Exact != nil {
|
||||
in, out := &in.Exact, &out.Exact
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForbiddenListSpec.
|
||||
func (in *ForbiddenListSpec) DeepCopy() *ForbiddenListSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ForbiddenListSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IngressOptions) DeepCopyInto(out *IngressOptions) {
|
||||
*out = *in
|
||||
if in.AllowedClasses != nil {
|
||||
in, out := &in.AllowedClasses, &out.AllowedClasses
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.AllowedHostnames != nil {
|
||||
in, out := &in.AllowedHostnames, &out.AllowedHostnames
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressOptions.
|
||||
func (in *IngressOptions) DeepCopy() *IngressOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(IngressOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LimitRangesSpec) DeepCopyInto(out *LimitRangesSpec) {
|
||||
*out = *in
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]corev1.LimitRangeSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LimitRangesSpec.
|
||||
func (in *LimitRangesSpec) DeepCopy() *LimitRangesSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LimitRangesSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NamespaceOptions) DeepCopyInto(out *NamespaceOptions) {
|
||||
*out = *in
|
||||
if in.Quota != nil {
|
||||
in, out := &in.Quota, &out.Quota
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.AdditionalMetadata != nil {
|
||||
in, out := &in.AdditionalMetadata, &out.AdditionalMetadata
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NamespaceOptions.
|
||||
func (in *NamespaceOptions) DeepCopy() *NamespaceOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NamespaceOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkPolicySpec) DeepCopyInto(out *NetworkPolicySpec) {
|
||||
*out = *in
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]networkingv1.NetworkPolicySpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkPolicySpec.
|
||||
func (in *NetworkPolicySpec) DeepCopy() *NetworkPolicySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkPolicySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in OwnerListSpec) DeepCopyInto(out *OwnerListSpec) {
|
||||
{
|
||||
in := &in
|
||||
*out = make(OwnerListSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OwnerListSpec.
|
||||
func (in OwnerListSpec) DeepCopy() OwnerListSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(OwnerListSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *OwnerSpec) DeepCopyInto(out *OwnerSpec) {
|
||||
*out = *in
|
||||
if in.ProxyOperations != nil {
|
||||
in, out := &in.ProxyOperations, &out.ProxyOperations
|
||||
*out = make([]ProxySettings, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OwnerSpec.
|
||||
func (in *OwnerSpec) DeepCopy() *OwnerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(OwnerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ProxySettings) DeepCopyInto(out *ProxySettings) {
|
||||
*out = *in
|
||||
if in.Operations != nil {
|
||||
in, out := &in.Operations, &out.Operations
|
||||
*out = make([]ProxyOperation, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProxySettings.
|
||||
func (in *ProxySettings) DeepCopy() *ProxySettings {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ProxySettings)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceQuotaSpec) DeepCopyInto(out *ResourceQuotaSpec) {
|
||||
*out = *in
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]corev1.ResourceQuotaSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceQuotaSpec.
|
||||
func (in *ResourceQuotaSpec) DeepCopy() *ResourceQuotaSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceQuotaSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ServiceOptions) DeepCopyInto(out *ServiceOptions) {
|
||||
*out = *in
|
||||
if in.AdditionalMetadata != nil {
|
||||
in, out := &in.AdditionalMetadata, &out.AdditionalMetadata
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.AllowedServices != nil {
|
||||
in, out := &in.AllowedServices, &out.AllowedServices
|
||||
*out = new(AllowedServices)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ExternalServiceIPs != nil {
|
||||
in, out := &in.ExternalServiceIPs, &out.ExternalServiceIPs
|
||||
*out = new(ExternalServiceIPsSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceOptions.
|
||||
func (in *ServiceOptions) DeepCopy() *ServiceOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ServiceOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Tenant) DeepCopyInto(out *Tenant) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Tenant.
|
||||
func (in *Tenant) DeepCopy() *Tenant {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Tenant)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *Tenant) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantList) DeepCopyInto(out *TenantList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]Tenant, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantList.
|
||||
func (in *TenantList) DeepCopy() *TenantList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *TenantList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
*out = *in
|
||||
if in.Owners != nil {
|
||||
in, out := &in.Owners, &out.Owners
|
||||
*out = make(OwnerListSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.NamespaceOptions != nil {
|
||||
in, out := &in.NamespaceOptions, &out.NamespaceOptions
|
||||
*out = new(NamespaceOptions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ServiceOptions != nil {
|
||||
in, out := &in.ServiceOptions, &out.ServiceOptions
|
||||
*out = new(ServiceOptions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StorageClasses != nil {
|
||||
in, out := &in.StorageClasses, &out.StorageClasses
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.IngressOptions.DeepCopyInto(&out.IngressOptions)
|
||||
if in.ContainerRegistries != nil {
|
||||
in, out := &in.ContainerRegistries, &out.ContainerRegistries
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NodeSelector != nil {
|
||||
in, out := &in.NodeSelector, &out.NodeSelector
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
in.NetworkPolicies.DeepCopyInto(&out.NetworkPolicies)
|
||||
in.LimitRanges.DeepCopyInto(&out.LimitRanges)
|
||||
in.ResourceQuota.DeepCopyInto(&out.ResourceQuota)
|
||||
if in.AdditionalRoleBindings != nil {
|
||||
in, out := &in.AdditionalRoleBindings, &out.AdditionalRoleBindings
|
||||
*out = make([]AdditionalRoleBindingsSpec, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.ImagePullPolicies != nil {
|
||||
in, out := &in.ImagePullPolicies, &out.ImagePullPolicies
|
||||
*out = make([]ImagePullPolicySpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.PriorityClasses != nil {
|
||||
in, out := &in.PriorityClasses, &out.PriorityClasses
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantSpec.
|
||||
func (in *TenantSpec) DeepCopy() *TenantSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantStatus) DeepCopyInto(out *TenantStatus) {
|
||||
*out = *in
|
||||
if in.Namespaces != nil {
|
||||
in, out := &in.Namespaces, &out.Namespaces
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TenantStatus.
|
||||
func (in *TenantStatus) DeepCopy() *TenantStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TenantStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -21,8 +21,8 @@ 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.0.18
|
||||
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.
|
||||
appVersion: 0.0.4
|
||||
appVersion: 0.1.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Deploying the Capsule Operator
|
||||
|
||||
Use the Capsule Operator for easily implementing, managing, and maintaining mutitenancy and access control in Kubernetes.
|
||||
Use the Capsule Operator for easily implementing, managing, and maintaining multitenancy and access control in Kubernetes.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -24,19 +24,23 @@ The Capsule Operator Chart can be used to instantly deploy the Capsule Operator
|
||||
|
||||
$ helm repo add clastix https://clastix.github.io/charts
|
||||
|
||||
2. Install the Chart:
|
||||
2. Create the Namespace:
|
||||
|
||||
$ kubectl create namespace capsule-system
|
||||
|
||||
3. Install the Chart:
|
||||
|
||||
$ helm install capsule clastix/capsule -n capsule-system
|
||||
|
||||
3. Show the status:
|
||||
4. Show the status:
|
||||
|
||||
$ helm status capsule -n capsule-system
|
||||
|
||||
4. Upgrade the Chart
|
||||
5. Upgrade the Chart
|
||||
|
||||
$ helm upgrade capsule clastix/capsule -n capsule-system
|
||||
|
||||
5. Uninstall the Chart
|
||||
6. Uninstall the Chart
|
||||
|
||||
$ helm uninstall capsule -n capsule-system
|
||||
|
||||
@@ -60,9 +64,10 @@ Here the values you can override:
|
||||
|
||||
Parameter | Description | Default
|
||||
--- | --- | ---
|
||||
`manager.hostNetwork` | Specifies if the container should be started in `hostNetwork` mode. | `false`
|
||||
`manager.options.logLevel` | Set the log verbosity of the controller with a value from 1 to 10.| `4`
|
||||
`manager.options.forceTenantPrefix` | Boolean, enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash | `false`
|
||||
`manager.options.capsuleUserGroup` | Override the Capsule user group | `capsule.clastix.io`
|
||||
`manager.options.capsuleUserGroups` | Override the Capsule user groups | `[capsule.clastix.io]`
|
||||
`manager.options.protectedNamespaceRegex` | If specified, disallows creation of namespaces matching the passed regexp | `null`
|
||||
`manager.image.repository` | Set the image repository of the controller. | `quay.io/clastix/capsule`
|
||||
`manager.image.tag` | Overrides the image tag whose default is the chart. `appVersion` | `null`
|
||||
@@ -73,13 +78,6 @@ Parameter | Description | Default
|
||||
`manager.resources.requests/memory` | Set the memory requests assigned to the controller. | `128Mi`
|
||||
`manager.resources.limits/cpu` | Set the CPU limits assigned to the controller. | `200m`
|
||||
`manager.resources.limits/cpu` | Set the memory limits assigned to the controller. | `128Mi`
|
||||
`proxy.image.repository` | Set the image repository of the rbac proxy. | `gcr.io/kubebuilder/kube-rbac-proxy`
|
||||
`proxy.image.tag` | Set the image tag of the rbac proxy. | `v0.5.0`
|
||||
`proxy.image.pullPolicy` | Set the image pull policy. | `IfNotPresent`
|
||||
`proxy.resources.requests/cpu` | Set the CPU requests assigned to the rbac proxy. | `10m`
|
||||
`proxy.resources.requests/memory` | Set the memory requests assigned to the rbac proxy. | `64Mi`
|
||||
`proxy.resources.limits/cpu` | Set the CPU limits assigned to the rbac proxy. | `100m`
|
||||
`proxy.resources.limits/cpu` | Set the memory limits assigned to the rbac proxy. | `128Mi`
|
||||
`mutatingWebhooksTimeoutSeconds` | Timeout in seconds for mutating webhooks. | `30`
|
||||
`validatingWebhooksTimeoutSeconds` | Timeout in seconds for validating webhooks. | `30`
|
||||
`imagePullSecrets` | Configuration for `imagePullSecrets` so that you can use a private images registry. | `[]`
|
||||
@@ -93,16 +91,24 @@ Parameter | Description | Default
|
||||
`replicaCount` | Set the replica count for Capsule pod. | `1`
|
||||
`affinity` | Set affinity rules for the Capsule pod. | `{}`
|
||||
`podSecurityPolicy.enabled` | Specify if a Pod Security Policy must be created. | `false`
|
||||
`serviceMonitor.enabled` | Specifies if a service monitor must be created. | `false`
|
||||
`serviceMonitor.labels` | Additional labels which will be added to service monitor. | `{}`
|
||||
`serviceMonitor.annotations` | Additional annotations which will be added to service monitor. | `{}`
|
||||
`serviceMonitor.matchLabels` | Additional matchLabels which will be added to service monitor. | `{}`
|
||||
`serviceMonitor.serviceAccount.name` | Specifies service account name for metrics scrape. | `capsule`
|
||||
`serviceMonitor.serviceAccount.namespace` | Specifies service account namespace for metrics scrape. | `capsule-system`
|
||||
`customLabels` | Additional labels which will be added to all resources created by Capsule helm chart . | `{}`
|
||||
`customAnnotations` | Additional annotations which will be added to all resources created by Capsule helm chart . | `{}`
|
||||
|
||||
## Created resources
|
||||
|
||||
This Helm Chart cretes the following Kubernetes resources in the release namespace:
|
||||
This Helm Chart creates the following Kubernetes resources in the release namespace:
|
||||
|
||||
* Capsule Namespace
|
||||
* Capsule Operator Deployment
|
||||
* Capsule Service
|
||||
* CA Secret
|
||||
* Certfificate Secret
|
||||
* Certificate Secret
|
||||
* Tenant Custom Resource Definition
|
||||
* MutatingWebHookConfiguration
|
||||
* ValidatingWebHookConfiguration
|
||||
@@ -112,8 +118,10 @@ This Helm Chart cretes the following Kubernetes resources in the release namespa
|
||||
And optionally, depending on the values set:
|
||||
|
||||
* Capsule ServiceAccount
|
||||
* Capsule Service Monitor
|
||||
* PodSecurityPolicy
|
||||
* RBAC ClusterRole and RoleBinding for pod security policy
|
||||
* RBAC Role and Rolebinding for metrics scrape
|
||||
|
||||
## Notes on installing Custom Resource Definitions with Helm3
|
||||
|
||||
|
||||
56
charts/capsule/crds/capsuleconfiguration-crd.yaml
Normal file
56
charts/capsule/crds/capsuleconfiguration-crd.yaml
Normal file
@@ -0,0 +1,56 @@
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.5.0
|
||||
creationTimestamp: null
|
||||
name: capsuleconfigurations.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
names:
|
||||
kind: CapsuleConfiguration
|
||||
listKind: CapsuleConfigurationList
|
||||
plural: capsuleconfigurations
|
||||
singular: capsuleconfiguration
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: CapsuleConfiguration is the Schema for the Capsule configuration API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: CapsuleConfigurationSpec defines the Capsule configuration
|
||||
properties:
|
||||
forceTenantPrefix:
|
||||
default: false
|
||||
description: Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
|
||||
type: boolean
|
||||
protectedNamespaceRegex:
|
||||
description: Disallow creation of namespaces, whose name matches this regexp
|
||||
type: string
|
||||
userGroups:
|
||||
default:
|
||||
- capsule.clastix.io
|
||||
description: Names of the groups for Capsule users.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,9 @@ helm.sh/chart: {{ include "capsule.chart" . }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- if .Values.customLabels }}
|
||||
{{ toYaml .Values.customLabels }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
@@ -50,6 +53,19 @@ app.kubernetes.io/name: {{ include "capsule.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
ServiceAccount annotations
|
||||
*/}}
|
||||
{{- define "capsule.serviceAccountAnnotations" -}}
|
||||
{{- if .Values.serviceAccount.annotations }}
|
||||
{{- toYaml .Values.serviceAccount.annotations }}
|
||||
{{- end }}
|
||||
{{- if .Values.customAnnotations }}
|
||||
{{ toYaml .Values.customAnnotations }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
|
||||
{{/*
|
||||
Create the name of the service account to use
|
||||
*/}}
|
||||
|
||||
@@ -3,5 +3,9 @@ kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
name: {{ include "capsule.secretCaName" . }}
|
||||
data:
|
||||
|
||||
@@ -3,5 +3,9 @@ kind: Secret
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
name: {{ include "capsule.secretTlsName" . }}
|
||||
data:
|
||||
|
||||
17
charts/capsule/templates/configuration-default.yaml
Normal file
17
charts/capsule/templates/configuration-default.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
apiVersion: capsule.clastix.io/v1alpha1
|
||||
kind: CapsuleConfiguration
|
||||
metadata:
|
||||
name: default
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
forceTenantPrefix: {{ .Values.manager.options.forceTenantPrefix }}
|
||||
userGroups:
|
||||
{{- range .Values.manager.options.capsuleUserGroups }}
|
||||
- {{ . }}
|
||||
{{- end}}
|
||||
protectedNamespaceRegex: {{ .Values.manager.options.protectedNamespaceRegex | quote }}
|
||||
@@ -4,6 +4,10 @@ metadata:
|
||||
name: {{ include "capsule.deploymentName" . }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
@@ -11,18 +15,21 @@ spec:
|
||||
{{- include "capsule.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
{{- with .Values.podAnnotations }}
|
||||
{{- with .Values.podAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
labels:
|
||||
{{- include "capsule.selectorLabels" . | nindent 8 }}
|
||||
{{- include "capsule.labels" . | nindent 8 }}
|
||||
spec:
|
||||
{{- with .Values.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
serviceAccountName: {{ include "capsule.serviceAccountName" . }}
|
||||
{{- if .Values.manager.hostNetwork }}
|
||||
hostNetwork: true
|
||||
{{- end }}
|
||||
priorityClassName: {{ .Values.priorityClassName }}
|
||||
{{- with .Values.nodeSelector }}
|
||||
nodeSelector:
|
||||
@@ -46,12 +53,9 @@ spec:
|
||||
command:
|
||||
- /manager
|
||||
args:
|
||||
- --metrics-addr=127.0.0.1:8080
|
||||
- --enable-leader-election
|
||||
- --zap-log-level={{ default 4 .Values.manager.options.logLevel }}
|
||||
{{ if .Values.manager.options.forceTenantPrefix }}- --force-tenant-prefix={{ .Values.manager.options.forceTenantPrefix }}{{ end }}
|
||||
{{ if .Values.manager.options.capsuleUserGroup }}- --capsule-user-group={{ .Values.manager.options.capsuleUserGroup }}{{ end }}
|
||||
{{ if .Values.manager.options.protectedNamespaceRegex }}- --protected-namespace-regex={{ .Values.manager.options.protectedNamespaceRegex }}{{ end }}
|
||||
- --configuration-name=default
|
||||
image: {{ include "capsule.managerFullyQualifiedDockerImage" . }}
|
||||
imagePullPolicy: {{ .Values.manager.image.pullPolicy }}
|
||||
env:
|
||||
@@ -63,6 +67,9 @@ spec:
|
||||
- name: webhook-server
|
||||
containerPort: 9443
|
||||
protocol: TCP
|
||||
- name: metrics
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
livenessProbe:
|
||||
{{- toYaml .Values.manager.livenessProbe | nindent 12}}
|
||||
readinessProbe:
|
||||
@@ -75,19 +82,3 @@ spec:
|
||||
{{- toYaml .Values.manager.resources | nindent 12 }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
- name: kube-rbac-proxy
|
||||
image: {{ include "capsule.proxyFullyQualifiedDockerImage" . }}
|
||||
imagePullPolicy: {{ .Values.proxy.image.pullPolicy }}
|
||||
args:
|
||||
- --secure-listen-address=0.0.0.0:8443
|
||||
- --upstream=http://127.0.0.1:8080/
|
||||
- --logtostderr=true
|
||||
- --v=10
|
||||
ports:
|
||||
- containerPort: 8443
|
||||
name: https
|
||||
protocol: TCP
|
||||
resources:
|
||||
{{- toYaml .Values.proxy.resources | nindent 12 }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
|
||||
46
charts/capsule/templates/metrics-rbac.yaml
Normal file
46
charts/capsule/templates/metrics-rbac.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
{{- if .Values.serviceMonitor.enabled }}
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- if .Values.serviceMonitor.labels }}
|
||||
{{- toYaml .Values.serviceMonitor.labels | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
name: {{ include "capsule.fullname" . }}-metrics-role
|
||||
namespace: {{ .Values.serviceMonitor.namespace | default .Release.Namespace }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
- endpoints
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- if .Values.serviceMonitor.labels }}
|
||||
{{- toYaml .Values.serviceMonitor.labels | nindent 4 }}
|
||||
{{- end }}
|
||||
name: {{ include "capsule.fullname" . }}-metrics-rolebinding
|
||||
namespace: {{ .Values.serviceMonitor.namespace | default .Release.Namespace }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ include "capsule.fullname" . }}-metrics-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .Values.serviceMonitor.serviceAccount.name }}
|
||||
namespace: {{ .Values.serviceMonitor.serviceAccount.namespace | default .Release.Namespace }}
|
||||
{{- end }}
|
||||
@@ -4,12 +4,16 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-controller-manager-metrics-service
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
ports:
|
||||
- port: 8443
|
||||
name: https
|
||||
- port: 8080
|
||||
name: metrics
|
||||
protocol: TCP
|
||||
targetPort: https
|
||||
targetPort: 8080
|
||||
selector:
|
||||
{{- include "capsule.selectorLabels" . | nindent 4 }}
|
||||
sessionAffinity: None
|
||||
|
||||
@@ -4,31 +4,37 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-mutating-webhook-configuration
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
webhooks:
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /mutate-v1-namespace-owner-reference
|
||||
path: /namespace-owner-reference
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
failurePolicy: {{ .Values.webhooks.namespaceOwnerReference.failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: owner.namespace.capsule.clastix.io
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
reinvocationPolicy: Never
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- namespaces
|
||||
scope: '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- namespaces
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ .Values.mutatingWebhooksTimeoutSeconds }}
|
||||
|
||||
@@ -5,6 +5,10 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
fsGroup:
|
||||
rule: RunAsAny
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: "{{ .Release.Name }}"
|
||||
name: "{{ .Release.Name }}-waiting-certs"
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion }}
|
||||
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
# This is what defines this resource as a hook. Without this line, the
|
||||
# job is considered part of the release.
|
||||
"helm.sh/hook": post-install
|
||||
"helm.sh/hook-weight": "-5"
|
||||
"helm.sh/hook-delete-policy": hook-succeeded
|
||||
{{- with .Values.customAnnotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
{{- $cmd := printf "kubectl scale deployment -n $NAMESPACE %s --replicas 0 &&" (include "capsule.deploymentName" .) -}}
|
||||
{{- $cmd = printf "%s kubectl delete secret -n $NAMESPACE %s %s --ignore-not-found &&" $cmd (include "capsule.secretTlsName" .) (include "capsule.secretCaName" .) -}}
|
||||
{{- $cmd = printf "%s kubectl delete clusterroles.rbac.authorization.k8s.io capsule-namespace-deleter capsule-namespace-provisioner --ignore-not-found &&" $cmd -}}
|
||||
{{- $cmd = printf "%s kubectl delete clusterrolebindings.rbac.authorization.k8s.io capsule-namespace-provisioner --ignore-not-found" $cmd -}}
|
||||
{{- $cmd = printf "%s kubectl delete clusterrolebindings.rbac.authorization.k8s.io capsule-namespace-deleter capsule-namespace-provisioner --ignore-not-found" $cmd -}}
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: "{{ .Release.Name }}"
|
||||
name: "{{ .Release.Name }}-rbac-cleaner"
|
||||
labels:
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name | quote }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion }}
|
||||
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
annotations:
|
||||
# This is what defines this resource as a hook. Without this line, the
|
||||
# job is considered part of the release.
|
||||
"helm.sh/hook": pre-delete
|
||||
"helm.sh/hook-weight": "-5"
|
||||
"helm.sh/hook-delete-policy": hook-succeeded
|
||||
{{- with .Values.customAnnotations }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
|
||||
@@ -4,6 +4,10 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-proxy-role
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- authentication.k8s.io
|
||||
@@ -24,6 +28,10 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-metrics-reader
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
rules:
|
||||
- nonResourceURLs:
|
||||
- /metrics
|
||||
@@ -36,6 +44,10 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-proxy-rolebinding
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
@@ -51,6 +63,10 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-manager-rolebinding
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
|
||||
@@ -5,8 +5,8 @@ metadata:
|
||||
name: {{ include "capsule.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceAccount.annotations }}
|
||||
{{- if or (.Values.serviceAccount.annotations) (.Values.customAnnotations) }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- include "capsule.serviceAccountAnnotations" . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
31
charts/capsule/templates/servicemonitor.yaml
Normal file
31
charts/capsule/templates/servicemonitor.yaml
Normal file
@@ -0,0 +1,31 @@
|
||||
{{- if .Values.serviceMonitor.enabled }}
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: {{ include "capsule.fullname" . }}-monitor
|
||||
namespace: {{ .Values.serviceMonitor.namespace | default .Release.Namespace }}
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.serviceMonitor.labels }}
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- with .Values.serviceMonitor.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
endpoints:
|
||||
- interval: 15s
|
||||
port: metrics
|
||||
path: /metrics
|
||||
jobLabel: app.kubernetes.io/name
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "capsule.labels" . | nindent 6 }}
|
||||
{{- with .Values.serviceMonitor.matchLabels }}
|
||||
{{- toYaml . | nindent 6 }}
|
||||
{{- end }}
|
||||
namespaceSelector:
|
||||
matchNames:
|
||||
- {{ .Release.Namespace }}
|
||||
{{- end }}
|
||||
@@ -4,263 +4,239 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-validating-webhook-configuration
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
webhooks:
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-ingress
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
name: ingress-v1beta1.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
- extensions
|
||||
apiVersions:
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- ingresses
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-ingress
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
name: ingress-v1.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- ingresses
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validate-v1-namespace-quota
|
||||
path: /cordoning
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
name: quota.namespace.capsule.clastix.io
|
||||
failurePolicy: {{ .Values.webhooks.cordoning.failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: cordoning.tenant.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
{{- toYaml .Values.webhooks.cordoning.namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- '*'
|
||||
apiVersions:
|
||||
- '*'
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- '*'
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /ingresses
|
||||
port: 443
|
||||
failurePolicy: {{ .Values.webhooks.ingresses.failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: ingress.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
{{- toYaml .Values.webhooks.ingresses.namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
- extensions
|
||||
apiVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- ingresses
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /namespaces
|
||||
port: 443
|
||||
failurePolicy: {{ .Values.webhooks.namespaces.failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: namespaces.capsule.clastix.io
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- namespaces
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- namespaces
|
||||
scope: '*'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-v1-network-policy
|
||||
path: /networkpolicies
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
name: validating.network-policy.capsule.clastix.io
|
||||
failurePolicy: {{ .Values.webhooks.networkpolicies.failurePolicy }}
|
||||
matchPolicy: Equivalent
|
||||
name: networkpolicies.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
{{- toYaml .Values.webhooks.networkpolicies.namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- networkpolicies
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
- apiGroups:
|
||||
- networking.k8s.io
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- networkpolicies
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-v1-pvc
|
||||
path: /pods
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
failurePolicy: {{ .Values.webhooks.pods.failurePolicy }}
|
||||
matchPolicy: Exact
|
||||
name: pods.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
{{- toYaml .Values.webhooks.pods.namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- pods
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /persistentvolumeclaims
|
||||
failurePolicy: {{ .Values.webhooks.persistentvolumeclaims.failurePolicy }}
|
||||
name: pvc.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
{{- toYaml .Values.webhooks.persistentvolumeclaims.namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- persistentvolumeclaims
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- persistentvolumeclaims
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-v1-tenant
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
name: tenant.capsule.clastix.io
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- capsule.clastix.io
|
||||
apiVersions:
|
||||
- v1alpha1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- tenants
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-v1-namespace-tenant-prefix
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
matchPolicy: Exact
|
||||
name: prefix.namespace.capsule.clastix.io
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- namespaces
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-v1-registry
|
||||
port: 443
|
||||
failurePolicy: Ignore
|
||||
matchPolicy: Exact
|
||||
name: pod.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
resources:
|
||||
- pods
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /validating-external-service-ips
|
||||
path: /services
|
||||
port: 443
|
||||
failurePolicy: Fail
|
||||
failurePolicy: {{ .Values.webhooks.services.failurePolicy }}
|
||||
matchPolicy: Exact
|
||||
name: validating-external-service-ips.capsule.clastix.io
|
||||
name: services.capsule.clastix.io
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
{{- toYaml .Values.webhooks.services.namespaceSelector | nindent 4}}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- services
|
||||
scope: '*'
|
||||
sideEffects: NoneOnDryRun
|
||||
- apiGroups:
|
||||
- ""
|
||||
apiVersions:
|
||||
- v1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- services
|
||||
scope: Namespaced
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
- v1beta1
|
||||
clientConfig:
|
||||
caBundle: Cg==
|
||||
service:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
namespace: {{ .Release.Namespace }}
|
||||
path: /tenants
|
||||
port: 443
|
||||
failurePolicy: {{ .Values.webhooks.tenants.failurePolicy }}
|
||||
matchPolicy: Exact
|
||||
name: tenants.capsule.clastix.io
|
||||
namespaceSelector: {}
|
||||
objectSelector: {}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- capsule.clastix.io
|
||||
apiVersions:
|
||||
- v1beta1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
- DELETE
|
||||
resources:
|
||||
- tenants
|
||||
scope: '*'
|
||||
sideEffects: None
|
||||
timeoutSeconds: {{ .Values.validatingWebhooksTimeoutSeconds }}
|
||||
|
||||
@@ -4,6 +4,10 @@ metadata:
|
||||
name: {{ include "capsule.fullname" . }}-webhook-service
|
||||
labels:
|
||||
{{- include "capsule.labels" . | nindent 4 }}
|
||||
{{- with .Values.customAnnotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
ports:
|
||||
- port: 443
|
||||
|
||||
@@ -7,12 +7,20 @@ manager:
|
||||
repository: quay.io/clastix/capsule
|
||||
pullPolicy: IfNotPresent
|
||||
tag: ''
|
||||
|
||||
# Specifies if the container should be started in hostNetwork mode.
|
||||
#
|
||||
# Required for use in some managed kubernetes clusters (such as AWS EKS) with custom
|
||||
# CNI (such as calico), because control-plane managed by AWS cannot communicate
|
||||
# with pods' IP CIDR and admission webhooks are not working
|
||||
hostNetwork: false
|
||||
|
||||
# Additional Capsule options
|
||||
options:
|
||||
logLevel: '4'
|
||||
forceTenantPrefix:
|
||||
capsuleUserGroup:
|
||||
protectedNamespaceRegex:
|
||||
forceTenantPrefix: false
|
||||
capsuleUserGroups: ["capsule.clastix.io"]
|
||||
protectedNamespaceRegex: ""
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
@@ -29,25 +37,11 @@ manager:
|
||||
requests:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
proxy:
|
||||
image:
|
||||
repository: gcr.io/kubebuilder/kube-rbac-proxy
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "v0.5.0"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 64Mi
|
||||
jobs:
|
||||
image:
|
||||
repository: bitnami/kubectl
|
||||
repository: quay.io/clastix/kubectl
|
||||
pullPolicy: IfNotPresent
|
||||
tag: "1.18"
|
||||
mutatingWebhooksTimeoutSeconds: 30
|
||||
validatingWebhooksTimeoutSeconds: 30
|
||||
tag: "v1.20.7"
|
||||
imagePullSecrets: []
|
||||
serviceAccount:
|
||||
create: true
|
||||
@@ -66,3 +60,68 @@ replicaCount: 1
|
||||
affinity: {}
|
||||
podSecurityPolicy:
|
||||
enabled: false
|
||||
|
||||
serviceMonitor:
|
||||
enabled: false
|
||||
# Install the ServiceMonitor into a different Namespace, as the monitoring stack one (default: the release one)
|
||||
namespace: ''
|
||||
# Assign additional labels according to Prometheus' serviceMonitorSelector matching labels
|
||||
labels: {}
|
||||
annotations: {}
|
||||
matchLabels: {}
|
||||
serviceAccount:
|
||||
name: capsule
|
||||
namespace: capsule-system
|
||||
|
||||
# Additional labels
|
||||
customLabels: {}
|
||||
|
||||
# Additional annotations
|
||||
customAnnotations: {}
|
||||
|
||||
# Webhooks configurations
|
||||
webhooks:
|
||||
namespaceOwnerReference:
|
||||
failurePolicy: Fail
|
||||
cordoning:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
ingresses:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
namespaces:
|
||||
failurePolicy: Fail
|
||||
networkpolicies:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
pods:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
persistentvolumeclaims:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
tenants:
|
||||
failurePolicy: Fail
|
||||
services:
|
||||
failurePolicy: Fail
|
||||
namespaceSelector:
|
||||
matchExpressions:
|
||||
- key: capsule.clastix.io/tenant
|
||||
operator: Exists
|
||||
mutatingWebhooksTimeoutSeconds: 30
|
||||
validatingWebhooksTimeoutSeconds: 30
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.5.0
|
||||
creationTimestamp: null
|
||||
name: capsuleconfigurations.capsule.clastix.io
|
||||
spec:
|
||||
group: capsule.clastix.io
|
||||
names:
|
||||
kind: CapsuleConfiguration
|
||||
listKind: CapsuleConfigurationList
|
||||
plural: capsuleconfigurations
|
||||
singular: capsuleconfiguration
|
||||
scope: Cluster
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: CapsuleConfiguration is the Schema for the Capsule configuration API
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: CapsuleConfigurationSpec defines the Capsule configuration
|
||||
properties:
|
||||
forceTenantPrefix:
|
||||
default: false
|
||||
description: Enforces the Tenant owner, during Namespace creation, to name it using the selected Tenant name as prefix, separated by a dash. This is useful to avoid Namespace name collision in a public CaaS environment.
|
||||
type: boolean
|
||||
protectedNamespaceRegex:
|
||||
description: Disallow creation of namespaces, whose name matches this regexp
|
||||
type: string
|
||||
userGroups:
|
||||
default:
|
||||
- capsule.clastix.io
|
||||
description: Names of the groups for Capsule users.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,8 +3,12 @@
|
||||
# It should be run by config/default
|
||||
resources:
|
||||
- bases/capsule.clastix.io_tenants.yaml
|
||||
- bases/capsule.clastix.io_capsuleconfigurations.yaml
|
||||
# +kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
# the following config is for teaching kustomize how to do kustomization for CRDs.
|
||||
configurations:
|
||||
- kustomizeconfig.yaml
|
||||
|
||||
patchesStrategicMerge:
|
||||
- patches/webhook_in_tenants.yaml
|
||||
|
||||
@@ -4,13 +4,15 @@ nameReference:
|
||||
version: v1
|
||||
fieldSpecs:
|
||||
- kind: CustomResourceDefinition
|
||||
version: v1
|
||||
group: apiextensions.k8s.io
|
||||
path: spec/conversion/webhookClientConfig/service/name
|
||||
path: spec/conversion/webhook/clientConfig/service/name
|
||||
|
||||
namespace:
|
||||
- kind: CustomResourceDefinition
|
||||
version: v1
|
||||
group: apiextensions.k8s.io
|
||||
path: spec/conversion/webhookClientConfig/service/namespace
|
||||
path: spec/conversion/webhook/clientConfig/service/namespace
|
||||
create: false
|
||||
|
||||
varReference:
|
||||
|
||||
17
config/crd/patches/webhook_in_tenants.yaml
Normal file
17
config/crd/patches/webhook_in_tenants.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
# The following patch enables a conversion webhook for the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: tenants.capsule.clastix.io
|
||||
spec:
|
||||
conversion:
|
||||
strategy: Webhook
|
||||
webhook:
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: system
|
||||
name: webhook-service
|
||||
path: /convert
|
||||
conversionReviewVersions:
|
||||
- v1alpha1
|
||||
- v1beta1
|
||||
@@ -22,8 +22,4 @@ bases:
|
||||
#- ../prometheus
|
||||
|
||||
patchesStrategicMerge:
|
||||
# Protect the /metrics endpoint by putting it behind auth.
|
||||
# If you want your controller-manager to expose the /metrics
|
||||
# endpoint w/o any authn/z, please comment the following line.
|
||||
- manager_auth_proxy_patch.yaml
|
||||
- manager_webhook_patch.yaml
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
# This patch inject a sidecar container which is a HTTP proxy for the
|
||||
# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: kube-rbac-proxy
|
||||
image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
|
||||
args:
|
||||
- "--secure-listen-address=0.0.0.0:8443"
|
||||
- "--upstream=http://127.0.0.1:8080/"
|
||||
- "--logtostderr=true"
|
||||
- "--v=10"
|
||||
ports:
|
||||
- containerPort: 8443
|
||||
name: https
|
||||
- name: manager
|
||||
args:
|
||||
- "--metrics-addr=127.0.0.1:8080"
|
||||
- "--enable-leader-election"
|
||||
@@ -12,6 +12,9 @@ spec:
|
||||
- containerPort: 9443
|
||||
name: webhook-server
|
||||
protocol: TCP
|
||||
- containerPort: 8080
|
||||
name: metrics
|
||||
protocol: TCP
|
||||
volumeMounts:
|
||||
- mountPath: /tmp/k8s-webhook-server/serving-certs
|
||||
name: cert
|
||||
|
||||
2129
config/grafana/dashboard.json
Normal file
2129
config/grafana/dashboard.json
Normal file
File diff suppressed because it is too large
Load Diff
1678
config/install.yaml
Normal file
1678
config/install.yaml
Normal file
File diff suppressed because it is too large
Load Diff
8
config/manager/configuration.yaml
Normal file
8
config/manager/configuration.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: capsule.clastix.io/v1alpha1
|
||||
kind: CapsuleConfiguration
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
userGroups: ["capsule.clastix.io"]
|
||||
forceTenantPrefix: false
|
||||
protectedNamespaceRegex: ""
|
||||
@@ -1,8 +1,10 @@
|
||||
resources:
|
||||
- configuration.yaml
|
||||
- manager.yaml
|
||||
- metrics_service.yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
images:
|
||||
- name: controller
|
||||
newName: quay.io/clastix/capsule
|
||||
newTag: v0.0.4
|
||||
newTag: v0.1.1-rc0
|
||||
|
||||
@@ -29,6 +29,7 @@ spec:
|
||||
- --enable-leader-election
|
||||
- --zap-encoder=console
|
||||
- --zap-log-level=debug
|
||||
- --configuration-name=capsule-default
|
||||
env:
|
||||
- name: NAMESPACE
|
||||
valueFrom:
|
||||
|
||||
@@ -7,8 +7,8 @@ metadata:
|
||||
namespace: system
|
||||
spec:
|
||||
ports:
|
||||
- name: https
|
||||
port: 8443
|
||||
targetPort: https
|
||||
- name: metrics
|
||||
port: 8080
|
||||
targetPort: metrics
|
||||
selector:
|
||||
control-plane: controller-manager
|
||||
@@ -1,2 +1,4 @@
|
||||
resources:
|
||||
- monitor.yaml
|
||||
- role.yaml
|
||||
- rolebinding.yaml
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
|
||||
# Prometheus Monitor Service (Metrics)
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
name: controller-manager-metrics-monitor
|
||||
name: capsule-monitor
|
||||
namespace: system
|
||||
spec:
|
||||
endpoints:
|
||||
- path: /metrics
|
||||
port: https
|
||||
- interval: 15s
|
||||
path: /metrics
|
||||
port: metrics
|
||||
jobLabel: controller-manager
|
||||
namespaceSelector:
|
||||
selector:
|
||||
matchLabels:
|
||||
control-plane: controller-manager
|
||||
|
||||
18
config/prometheus/role.yaml
Normal file
18
config/prometheus/role.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
name: capsule-metrics-role
|
||||
namespace: capsule-system
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
- endpoints
|
||||
- pods
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
15
config/prometheus/rolebinding.yaml
Normal file
15
config/prometheus/rolebinding.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
control-plane: controller-manager
|
||||
name: capsule-metrics-rolebinding
|
||||
namespace: system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: capsule-metrics-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: capsule
|
||||
namespace: capsule-system
|
||||
@@ -1,7 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: metrics-reader
|
||||
rules:
|
||||
- nonResourceURLs: ["/metrics"]
|
||||
verbs: ["get"]
|
||||
@@ -1,13 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: proxy-role
|
||||
rules:
|
||||
- apiGroups: ["authentication.k8s.io"]
|
||||
resources:
|
||||
- tokenreviews
|
||||
verbs: ["create"]
|
||||
- apiGroups: ["authorization.k8s.io"]
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs: ["create"]
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: proxy-rolebinding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: proxy-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: default
|
||||
namespace: system
|
||||
@@ -1,12 +1,5 @@
|
||||
resources:
|
||||
- role_binding.yaml
|
||||
# Comment the following 4 lines if you want to disable
|
||||
# the auth proxy (https://github.com/brancz/kube-rbac-proxy)
|
||||
# which protects your /metrics endpoint.
|
||||
- auth_proxy_service.yaml
|
||||
- auth_proxy_role.yaml
|
||||
- auth_proxy_role_binding.yaml
|
||||
- auth_proxy_client_clusterrole.yaml
|
||||
# Uncomment the following 3 lines if you are running Capsule
|
||||
# in a cluster where [Pod Security Policies](https://kubernetes.io/docs/concepts/policy/pod-security-policy/)
|
||||
# are enabled.
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
apiVersion: capsule.clastix.io/v1alpha1
|
||||
kind: CapsuleConfiguration
|
||||
metadata:
|
||||
name: default
|
||||
spec:
|
||||
userGroups: ["capsule.clastix.io"]
|
||||
forceTenantPrefix: false
|
||||
protectedNamespaceRegex: ""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user