mirror of
https://github.com/projectcapsule/capsule.git
synced 2026-03-02 09:40:18 +00:00
Compare commits
6 Commits
helm-v0.4.
...
maxgio92/f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
857c338c53 | ||
|
|
5a9c25b125 | ||
|
|
3cd7bfe6d4 | ||
|
|
ff53cc2f38 | ||
|
|
852ab16323 | ||
|
|
9c18471879 |
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
5
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -9,7 +9,10 @@ assignees: ''
|
||||
|
||||
<!--
|
||||
Thanks for taking time reporting a Capsule bug!
|
||||
|
||||
|
||||
We do our best to keep it reliable and working, so don't hesitate adding
|
||||
as many information as you can and keep in mind you can reach us on our
|
||||
Clastix Slack workspace: https://clastix.slack.com, #capsule channel.
|
||||
-->
|
||||
|
||||
# Bug description
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Chat on Slack
|
||||
url: https://kubernetes.slack.com/archives/C03GETTJQRL
|
||||
about: Maybe chatting with the community can help
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@@ -14,6 +14,8 @@ We're trying to build a community drive Open Source project, so don't
|
||||
hesitate proposing your enhancement ideas: keep in mind, since we would like
|
||||
to keep it as agnostic as possible, to motivate all your assumptions.
|
||||
|
||||
If you need to reach the maintainers, please join the Clastix Slack workspace:
|
||||
https://clastix.slack.com, #capsule channel.
|
||||
-->
|
||||
|
||||
# Describe the feature
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -15,4 +15,6 @@ following ourselves these points:
|
||||
- explain what and why in the body, if more than a trivial change, wrapping at
|
||||
72 characters
|
||||
|
||||
If you have any issue or question, reach out us!
|
||||
https://clastix.slack.com >>> #capsule channel
|
||||
-->
|
||||
|
||||
21
.github/actions/exists/action.yaml
vendored
21
.github/actions/exists/action.yaml
vendored
@@ -1,21 +0,0 @@
|
||||
name: Checks if an input is defined
|
||||
|
||||
description: Checks if an input is defined and outputs 'true' or 'false'.
|
||||
|
||||
inputs:
|
||||
value:
|
||||
description: value to test
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
result:
|
||||
description: outputs 'true' or 'false' if input value is defined or not
|
||||
value: ${{ steps.check.outputs.result }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- shell: bash
|
||||
id: check
|
||||
run: |
|
||||
echo "result=${{ inputs.value != '' }}" >> $GITHUB_OUTPUT
|
||||
20
.github/actions/setup-caches/action.yaml
vendored
20
.github/actions/setup-caches/action.yaml
vendored
@@ -1,20 +0,0 @@
|
||||
name: Setup caches
|
||||
|
||||
description: Setup caches for go modules and build cache.
|
||||
|
||||
inputs:
|
||||
build-cache-key:
|
||||
description: build cache prefix
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- uses: actions/cache@4723a57e26efda3a62cbde1812113b730952852d # v3.2.2
|
||||
with:
|
||||
path: ~/go/pkg/mod
|
||||
key: ${{ runner.os }}-go-pkg-mod-${{ hashFiles('**/go.sum') }}-${{ hashFiles('Makefile') }}
|
||||
- uses: actions/cache@4723a57e26efda3a62cbde1812113b730952852d # v3.2.2
|
||||
if: ${{ inputs.build-cache-key }}
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{ runner.os }}-build-cache-${{ inputs.build-cache-key }}-${{ hashFiles('**/go.sum') }}-${{ hashFiles('Makefile') }}
|
||||
10
.github/configs/ct.yaml
vendored
10
.github/configs/ct.yaml
vendored
@@ -1,10 +0,0 @@
|
||||
remote: origin
|
||||
target-branch: main
|
||||
chart-dirs:
|
||||
- charts
|
||||
helm-extra-args: "--timeout 600s"
|
||||
validate-chart-schema: false
|
||||
validate-maintainers: false
|
||||
validate-yaml: true
|
||||
exclude-deprecated: true
|
||||
check-version-increment: false
|
||||
43
.github/configs/lintconf.yaml
vendored
43
.github/configs/lintconf.yaml
vendored
@@ -1,43 +0,0 @@
|
||||
|
||||
---
|
||||
rules:
|
||||
braces:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 0
|
||||
min-spaces-inside-empty: -1
|
||||
max-spaces-inside-empty: -1
|
||||
brackets:
|
||||
min-spaces-inside: 0
|
||||
max-spaces-inside: 0
|
||||
min-spaces-inside-empty: -1
|
||||
max-spaces-inside-empty: -1
|
||||
colons:
|
||||
max-spaces-before: 0
|
||||
max-spaces-after: 1
|
||||
commas:
|
||||
max-spaces-before: 0
|
||||
min-spaces-after: 1
|
||||
max-spaces-after: 1
|
||||
comments:
|
||||
require-starting-space: true
|
||||
min-spaces-from-content: 1
|
||||
document-end: disable
|
||||
document-start: disable # No --- to start a file
|
||||
empty-lines:
|
||||
max: 2
|
||||
max-start: 0
|
||||
max-end: 0
|
||||
hyphens:
|
||||
max-spaces-after: 1
|
||||
indentation:
|
||||
spaces: consistent
|
||||
indent-sequences: whatever # - list indentation will handle both indentation and without
|
||||
check-multi-line-strings: false
|
||||
key-duplicates: enable
|
||||
line-length: disable # Lines can be any length
|
||||
new-line-at-end-of-file: enable
|
||||
new-lines:
|
||||
type: unix
|
||||
trailing-spaces: enable
|
||||
truthy:
|
||||
level: warning
|
||||
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
@@ -1,16 +0,0 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
rebase-strategy: disabled
|
||||
commit-message:
|
||||
prefix: "feat(deps)"
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
rebase-strategy: disabled
|
||||
commit-message:
|
||||
prefix: "ci"
|
||||
11
.github/maintainers.yaml
vendored
11
.github/maintainers.yaml
vendored
@@ -2,22 +2,17 @@
|
||||
github: https://github.com/bsctl
|
||||
company: Clastix
|
||||
projects:
|
||||
- https://github.com/projectcapsule/capsule
|
||||
- https://github.com/clastix/capsule
|
||||
- https://github.com/clastix/capsule-proxy
|
||||
- name: Dario Tranchitella
|
||||
github: https://github.com/prometherion
|
||||
company: Clastix
|
||||
projects:
|
||||
- https://github.com/projectcapsule/capsule
|
||||
- https://github.com/clastix/capsule
|
||||
- https://github.com/clastix/capsule-proxy
|
||||
- name: Maksim Fedotov
|
||||
github: https://github.com/MaxFedotov
|
||||
company: wargaming.net
|
||||
projects:
|
||||
- https://github.com/projectcapsule/capsule
|
||||
- https://github.com/clastix/capsule
|
||||
- https://github.com/clastix/capsule-proxy
|
||||
- name: Oliver Bähler
|
||||
github: https://github.com/oliverbaehler
|
||||
company: Bedag Informatik AG
|
||||
projects:
|
||||
- https://github.com/projectcapsule/capsule
|
||||
|
||||
24
.github/workflows/check-actions.yml
vendored
24
.github/workflows/check-actions.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Check actions
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Ensure SHA pinned actions
|
||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@f32435541e24cd6a4700a7f52bb2ec59e80603b1 # v2.1.4
|
||||
with:
|
||||
# slsa-github-generator requires using a semver tag for reusable workflows.
|
||||
# See: https://github.com/slsa-framework/slsa-github-generator#referencing-slsa-builders-and-generators
|
||||
allowlist: |
|
||||
slsa-framework/slsa-github-generator
|
||||
23
.github/workflows/check-commit.yml
vendored
23
.github/workflows/check-commit.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Check Commit
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
commit_lint:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: wagoid/commitlint-github-action@6319f54d83768b60acd6fd60e61007ccc583e62f #v5.4.3
|
||||
with:
|
||||
firstParent: true
|
||||
@@ -1,5 +1,4 @@
|
||||
name: Diff checks
|
||||
permissions: {}
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -7,27 +6,40 @@ on:
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
diff:
|
||||
name: diff
|
||||
runs-on: ubuntu-20.04
|
||||
commit_lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
- uses: wagoid/commitlint-github-action@v2
|
||||
with:
|
||||
go-version: '1.19'
|
||||
firstParent: true
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v2.3.0
|
||||
with:
|
||||
version: v1.45.2
|
||||
only-new-issues: false
|
||||
args: --timeout 2m --config .golangci.yml
|
||||
diff:
|
||||
name: diff
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18'
|
||||
- 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
|
||||
- run: make apidoc
|
||||
- name: Checking if the CRDs documentation is not aligned
|
||||
run: if [[ $(git diff | wc -l) -gt 0 ]]; then echo ">>> CRDs generated documentation 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)"
|
||||
- name: Checking if source code is not formatted
|
||||
38
.github/workflows/codecov.yml
vendored
38
.github/workflows/codecov.yml
vendored
@@ -1,38 +0,0 @@
|
||||
name: Codecov
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
codecov:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup caches
|
||||
uses: ./.github/actions/setup-caches
|
||||
timeout-minutes: 5
|
||||
continue-on-error: true
|
||||
with:
|
||||
build-cache-key: codecov
|
||||
- name: Check secret
|
||||
id: checksecret
|
||||
uses: ./.github/actions/exists
|
||||
with:
|
||||
value: ${{ secrets.CODECOV_TOKEN }}
|
||||
- name: Generate Code Coverage Report
|
||||
if: steps.checksecret.outputs.result == 'true'
|
||||
run: make test
|
||||
- name: Upload Report to Codecov
|
||||
if: steps.checksecret.outputs.result == 'true'
|
||||
uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
|
||||
with:
|
||||
file: ./coverage.out
|
||||
fail_ci_if_error: true
|
||||
verbose: true
|
||||
97
.github/workflows/docker-ci.yml
vendored
Normal file
97
.github/workflows/docker-ci.yml
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
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 }}
|
||||
docker.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: Login to docker.io Container Registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: docker.io
|
||||
username: ${{ secrets.USER_DOCKER_IO }}
|
||||
password: ${{ secrets.BOT_DOCKER_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 }}
|
||||
69
.github/workflows/docker-publish.yml
vendored
69
.github/workflows/docker-publish.yml
vendored
@@ -1,69 +0,0 @@
|
||||
name: Publish images
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
publish-images:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
id-token: write
|
||||
outputs:
|
||||
capsule-digest: ${{ steps.publish-capsule.outputs.digest }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup caches
|
||||
uses: ./.github/actions/setup-caches
|
||||
timeout-minutes: 5
|
||||
continue-on-error: true
|
||||
with:
|
||||
build-cache-key: publish-images
|
||||
- name: Run Trivy vulnerability (Repo)
|
||||
uses: aquasecurity/trivy-action@fbd16365eb88e12433951383f5e99bd901fc618f # v0.12.0
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
ignore-unfixed: true
|
||||
format: 'sarif'
|
||||
output: 'trivy-results.sarif'
|
||||
severity: 'CRITICAL,HIGH'
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
|
||||
- name: Publish Capsule
|
||||
id: publish-capsule
|
||||
uses: oliverbaehler/github-actions/ko-publish-image@979018716f7d0cbe8d2711f572b350afad4ef211 # v0.1.1
|
||||
with:
|
||||
makefile-target: ko-publish-capsule
|
||||
registry: ghcr.io
|
||||
registry-username: ${{ github.actor }}
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
repository: ${{ github.repository_owner }}
|
||||
version: ${{ github.ref_name }}
|
||||
sign-image: true
|
||||
sbom-name: capsule
|
||||
sbom-repository: ghcr.io/${{ github.repository_owner }}/sbom
|
||||
signature-repository: ghcr.io/${{ github.repository_owner }}/signatures
|
||||
main-path: ./
|
||||
env:
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
generate-capsule-provenance:
|
||||
needs: publish-images
|
||||
permissions:
|
||||
id-token: write # To sign the provenance.
|
||||
packages: write # To upload assets to release.
|
||||
actions: read # To read the workflow path.
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
|
||||
with:
|
||||
image: ghcr.io/${{ github.repository_owner }}/capsule
|
||||
digest: "${{ needs.publish-images.outputs.capsule-digest }}"
|
||||
registry-username: ${{ github.actor }}
|
||||
secrets:
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
31
.github/workflows/docs-lint.yml
vendored
31
.github/workflows/docs-lint.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: docs-lint
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
paths:
|
||||
- '.github/workflows/docs-lint.yml'
|
||||
- 'docs/content/**'
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
paths:
|
||||
- '.github/workflows/docs-lint.yml'
|
||||
- 'docs/content/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Spell Check
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1
|
||||
with:
|
||||
node-version: 18
|
||||
- run: make docs-lint
|
||||
19
.github/workflows/e2e.yml
vendored
19
.github/workflows/e2e.yml
vendored
@@ -1,5 +1,4 @@
|
||||
name: e2e
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -27,35 +26,31 @@ on:
|
||||
- 'main.go'
|
||||
- 'Makefile'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
kind:
|
||||
name: Kubernetes
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
k8s-version: ['v1.20.7', 'v1.21.2', 'v1.22.4', 'v1.23.6', 'v1.24.7', 'v1.25.3', 'v1.26.3', 'v1.27.2']
|
||||
runs-on: ubuntu-20.04
|
||||
k8s-version: ['v1.16.15', 'v1.17.11', 'v1.18.8', 'v1.19.4', 'v1.20.7', 'v1.21.2', 'v1.22.4', 'v1.23.6', 'v1.24.1']
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.19'
|
||||
go-version: '1.18'
|
||||
- run: make manifests
|
||||
- name: Checking if manifests are disaligned
|
||||
run: test -z "$(git diff 2> /dev/null)"
|
||||
- name: Checking if manifests generated untracked files
|
||||
run: test -z "$(git ls-files --others --exclude-standard 2> /dev/null)"
|
||||
- uses: engineerd/setup-kind@aa272fe2a7309878ffc2a81c56cfe3ef108ae7d0 # v0.5.0
|
||||
- uses: engineerd/setup-kind@v0.5.0
|
||||
with:
|
||||
skipClusterCreation: true
|
||||
version: v0.14.0
|
||||
- uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3
|
||||
- uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: 3.3.4
|
||||
- name: e2e testing
|
||||
|
||||
35
.github/workflows/fossa.yml
vendored
35
.github/workflows/fossa.yml
vendored
@@ -1,35 +0,0 @@
|
||||
name: FOSSA
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
fossa-scan:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout Code"
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Check secret
|
||||
id: checksecret
|
||||
uses: ./.github/actions/exists
|
||||
with:
|
||||
value: ${{ secrets.FOSSA_API_KEY }}
|
||||
- name: "Run FOSSA Scan"
|
||||
if: steps.checksecret.outputs.result == 'true'
|
||||
uses: fossas/fossa-action@f61a4c0c263690f2ddb54b9822a719c25a7b608f # v1.3.1
|
||||
with:
|
||||
api-key: ${{ secrets.FOSSA_API_KEY }}
|
||||
- name: "Run FOSSA Test"
|
||||
if: steps.checksecret.outputs.result == 'true'
|
||||
uses: fossas/fossa-action@f61a4c0c263690f2ddb54b9822a719c25a7b608f # v1.3.1
|
||||
with:
|
||||
api-key: ${{ secrets.FOSSA_API_KEY }}
|
||||
run-tests: true
|
||||
12
.github/workflows/gosec.yml
vendored
12
.github/workflows/gosec.yml
vendored
@@ -1,24 +1,18 @@
|
||||
name: CI gosec
|
||||
permissions: {}
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
uses: actions/checkout@v2
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: securego/gosec@0ec6cd95d7bf02aef4ec2786e884868e0044875b # v2.18.1
|
||||
uses: securego/gosec@master
|
||||
with:
|
||||
args: ./...
|
||||
|
||||
64
.github/workflows/helm-publish.yml
vendored
64
.github/workflows/helm-publish.yml
vendored
@@ -1,64 +0,0 @@
|
||||
name: Publish charts
|
||||
permissions: read-all
|
||||
on:
|
||||
push:
|
||||
tags: [ "helm-v*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
publish-helm:
|
||||
# Skip this Release on forks
|
||||
if: github.repository_owner == 'projectcapsule'
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Publish Helm chart
|
||||
uses: stefanprodan/helm-gh-pages@0ad2bb377311d61ac04ad9eb6f252fb68e207260 # v1.7.0
|
||||
with:
|
||||
token: "${{ secrets.HELM_CHARTS_PUSH_TOKEN }}"
|
||||
linting: off
|
||||
charts_dir: charts
|
||||
charts_url: https://${{ github.repository_owner }}.github.io/charts
|
||||
owner: ${{ github.repository_owner }}
|
||||
repository: charts
|
||||
branch: gh-pages
|
||||
commit_username: ${{ github.actor }}
|
||||
publish-helm-oci:
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
packages: write
|
||||
outputs:
|
||||
chart-digest: ${{ steps.helm_publish.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
|
||||
- name: Helm | Publish
|
||||
id: helm_publish
|
||||
uses: oliverbaehler/github-actions/helm-oci-chart@8dfd42735c85f6c58d5d4d6f3232cd0e39d1fe73 # v0.1.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
repository: ${{ github.repository_owner }}/charts
|
||||
name: "capsule"
|
||||
registry-username: ${{ github.actor }}
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
update-dependencies: 'true' # Defaults to false
|
||||
sign-image: 'true'
|
||||
signature-repository: ghcr.io/${{ github.repository_owner }}/signatures
|
||||
helm-provenance:
|
||||
needs: publish-helm-oci
|
||||
permissions:
|
||||
id-token: write # To sign the provenance.
|
||||
packages: write # To upload assets to release.
|
||||
actions: read # To read the workflow path.
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v1.9.0
|
||||
with:
|
||||
image: ghcr.io/${{ github.repository_owner }}/charts/capsule
|
||||
digest: "${{ needs.publish-helm-oci.outputs.chart-digest }}"
|
||||
registry-username: ${{ github.actor }}
|
||||
secrets:
|
||||
registry-password: ${{ secrets.GITHUB_TOKEN }}
|
||||
69
.github/workflows/helm-test.yml
vendored
69
.github/workflows/helm-test.yml
vendored
@@ -1,69 +0,0 @@
|
||||
name: Test charts
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: azure/setup-helm@5119fcb9089d432beecbf79bb2c7915207344b78 # v3
|
||||
- name: Linting Chart
|
||||
run: helm lint ./charts/capsule
|
||||
- name: Setup Chart Linting
|
||||
id: lint
|
||||
uses: helm/chart-testing-action@e8788873172cb653a90ca2e819d79d65a66d4e76 # v2.4.0
|
||||
- name: Run chart-testing (list-changed)
|
||||
id: list-changed
|
||||
run: |
|
||||
changed=$(ct list-changed --config ./.github/configs/ct.yaml)
|
||||
if [[ -n "$changed" ]]; then
|
||||
echo "::set-output name=changed::true"
|
||||
fi
|
||||
- name: Run chart-testing (lint)
|
||||
run: ct lint --debug --config ./.github/configs/ct.yaml --lint-conf ./.github/configs/lintconf.yaml
|
||||
- name: Run docs-testing (helm-docs)
|
||||
id: helm-docs
|
||||
run: |
|
||||
make helm-docs
|
||||
if [[ $(git diff --stat) != '' ]]; then
|
||||
echo -e '\033[0;31mDocumentation outdated! (Run make helm-docs locally and commit)\033[0m ❌'
|
||||
git diff --color
|
||||
exit 1
|
||||
else
|
||||
echo -e '\033[0;32mDocumentation up to date\033[0m ✔'
|
||||
fi
|
||||
|
||||
# ATTENTION: This is a workaround for the upcoming ApiVersion Conversions for the capsule CRDs
|
||||
# With this workflow the current docker image is build and loaded into kind, otherwise the install fails
|
||||
# In the future this must be removed and the chart-testing-action must be used
|
||||
- name: Run chart-testing (install)
|
||||
run: make helm-test
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
|
||||
## Create KIND Cluster
|
||||
- name: Create kind cluster
|
||||
uses: helm/kind-action@dda0770415bac9fc20092cacbc54aa298604d140 # v1.8.0
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
# Install Required Operators/CRDs
|
||||
- name: Prepare Cluster Operators/CRDs
|
||||
run: |
|
||||
# Cert-Manager CRDs
|
||||
kubectl create -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.crds.yaml
|
||||
|
||||
# Prometheus CRDs
|
||||
kubectl create -f https://github.com/prometheus-operator/prometheus-operator/releases/download/v0.58.0/bundle.yaml
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
# Install Charts
|
||||
- name: Run chart-testing (install)
|
||||
run: ct install --debug --config ./.github/configs/ct.yaml
|
||||
if: steps.list-changed.outputs.changed == 'true'
|
||||
36
.github/workflows/helm.yml
vendored
Normal file
36
.github/workflows/helm.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
name: Helm Chart
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
tags: [ "helm-v*" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: azure/setup-helm@v1
|
||||
with:
|
||||
version: 3.3.4
|
||||
- name: Linting Chart
|
||||
run: helm lint ./charts/capsule
|
||||
release:
|
||||
if: startsWith(github.ref, 'refs/tags/helm-v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Publish Helm chart
|
||||
uses: stefanprodan/helm-gh-pages@master
|
||||
with:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
charts_dir: charts
|
||||
charts_url: https://clastix.github.io/charts
|
||||
owner: clastix
|
||||
repository: charts
|
||||
branch: gh-pages
|
||||
target_dir: .
|
||||
commit_username: prometherion
|
||||
commit_email: dario@tranchitella.eu
|
||||
25
.github/workflows/lint.yml
vendored
25
.github/workflows/lint.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Linting
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
name: lint
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@3a919529898de77ec3da873e3063ca4b10e7f5cc # v3.7.0
|
||||
with:
|
||||
version: v1.51.2
|
||||
only-new-issues: false
|
||||
args: --timeout 5m --config .golangci.yml
|
||||
36
.github/workflows/releaser.yml
vendored
36
.github/workflows/releaser.yml
vendored
@@ -1,36 +0,0 @@
|
||||
name: Go Release
|
||||
|
||||
permissions: {}
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Setup caches
|
||||
uses: ./.github/actions/setup-caches
|
||||
timeout-minutes: 5
|
||||
continue-on-error: true
|
||||
- uses: creekorful/goreportcard-action@1f35ced8cdac2cba28c9a2f2288a16aacfd507f9 # v1.0
|
||||
- uses: anchore/sbom-action/download-syft@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
|
||||
- name: Run GoReleaser
|
||||
uses: goreleaser/goreleaser-action@7ec5c2b0c6cdda6e8bbb49444bc797dd33d74dd8 # v5.0.0
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --timeout 90m --debug
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
42
.github/workflows/scorecard.yml
vendored
42
.github/workflows/scorecard.yml
vendored
@@ -1,42 +0,0 @@
|
||||
name: Scorecards supply-chain security
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 5'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
id-token: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Run analysis
|
||||
uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
repo_token: ${{ secrets.SCORECARD_READ_TOKEN }}
|
||||
publish_results: true
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
- name: Upload to code-scanning
|
||||
uses: github/codeql-action/upload-sarif@cdcdbb579706841c47f7063dda365e292e5cad7a # v2.13.4
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,7 +6,6 @@
|
||||
*.so
|
||||
*.dylib
|
||||
bin
|
||||
dist/
|
||||
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
@@ -30,4 +29,4 @@ dist/
|
||||
**/*.key
|
||||
.DS_Store
|
||||
*.tgz
|
||||
kind.yaml
|
||||
|
||||
|
||||
@@ -14,12 +14,7 @@ linters-settings:
|
||||
sections:
|
||||
- standard
|
||||
- default
|
||||
- prefix(github.com/projectcapsule/capsule)
|
||||
goheader:
|
||||
template: |-
|
||||
Copyright 2020-2023 Project Capsule Authors.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
- prefix(github.com/clastix/capsule)
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
@@ -39,16 +34,13 @@ linters:
|
||||
- testpackage
|
||||
- varnamelen
|
||||
- wrapcheck
|
||||
- exhaustruct
|
||||
- varcheck
|
||||
- structcheck
|
||||
- nosnakecase
|
||||
- deadcode
|
||||
- ifshort
|
||||
- nonamedreturns
|
||||
|
||||
issues:
|
||||
exclude:
|
||||
- Using the variable on range scope .* in function literal
|
||||
|
||||
service:
|
||||
golangci-lint-version: 1.51.2
|
||||
golangci-lint-version: 1.33.x
|
||||
|
||||
run:
|
||||
skip-files:
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
project_name: capsule
|
||||
env:
|
||||
- COSIGN_EXPERIMENTAL=true
|
||||
- GO111MODULE=on
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
gomod:
|
||||
proxy: false
|
||||
builds:
|
||||
- main: .
|
||||
binary: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
goos:
|
||||
- linux
|
||||
flags:
|
||||
- -trimpath
|
||||
mod_timestamp: '{{ .CommitTimestamp }}'
|
||||
ldflags:
|
||||
- >-
|
||||
-X main.Version={{ .Tag }}
|
||||
-X main.GitCommit={{ .Commit }}
|
||||
-X main.GitTag={{ .Tag }}
|
||||
-X main.GitTreeState={{ .Date }}
|
||||
-X main.BuildDate={{ .Date }}
|
||||
-X main.GitRepo={{ .ProjectName }}
|
||||
release:
|
||||
prerelease: auto
|
||||
footer: |
|
||||
Thanks to all the contributors!
|
||||
|
||||
**Full Changelog**: https://github.com/projectcapsule/{{ .ProjectName }}/compare/{{ .PreviousTag }}...{{ .Tag }}
|
||||
|
||||
**Docker Images**
|
||||
- `ghcr.io/projectcapsule/{{ .ProjectName }}:{{ .Tag }}`
|
||||
- `ghcr.io/projectcapsule/{{ .ProjectName }}:latest`
|
||||
checksum:
|
||||
name_template: 'checksums.txt'
|
||||
changelog:
|
||||
sort: asc
|
||||
use: github
|
||||
filters:
|
||||
exclude:
|
||||
- '^test:'
|
||||
- '^chore'
|
||||
- '^rebase:'
|
||||
- 'merge conflict'
|
||||
- Merge pull request
|
||||
- Merge remote-tracking branch
|
||||
- Merge branch
|
||||
groups:
|
||||
# https://github.com/conventional-changelog/commitlint/tree/master/%40commitlint/config-conventional
|
||||
- title: '🛠 Dependency updates'
|
||||
regexp: '^.*?(feat|fix)\(deps\)!?:.+$'
|
||||
order: 300
|
||||
- title: '✨ New Features'
|
||||
regexp: '^.*?feat(\([[:word:]]+\))??!?:.+$'
|
||||
order: 100
|
||||
- title: '🐛 Bug fixes'
|
||||
regexp: '^.*?fix(\([[:word:]]+\))??!?:.+$'
|
||||
order: 200
|
||||
- title: '📖 Documentation updates'
|
||||
regexp: ^.*?docs(\([[:word:]]+\))??!?:.+$
|
||||
order: 400
|
||||
- title: '🚀 Build process updates'
|
||||
regexp: ^.*?(build|ci)(\([[:word:]]+\))??!?:.+$
|
||||
order: 400
|
||||
- title: '📦 Other work'
|
||||
order: 9999
|
||||
sboms:
|
||||
- artifacts: archive
|
||||
signs:
|
||||
- cmd: cosign
|
||||
args:
|
||||
- "sign-blob"
|
||||
- "--output-signature=${signature}"
|
||||
- "${artifact}"
|
||||
- "--yes"
|
||||
artifacts: all
|
||||
8
.ko.yaml
8
.ko.yaml
@@ -1,8 +0,0 @@
|
||||
defaultPlatforms:
|
||||
- linux/arm64
|
||||
- linux/amd64
|
||||
builds:
|
||||
- id: capsule
|
||||
main: ./
|
||||
ldflags:
|
||||
- '{{ if index .Env "LD_FLAGS" }}{{ .Env.LD_FLAGS }}{{ end }}'
|
||||
27
ADOPTERS.md
27
ADOPTERS.md
@@ -3,30 +3,3 @@
|
||||
This is a list of companies that have adopted Capsule, feel free to open a Pull-Request to get yours listed.
|
||||
|
||||
## Adopters list (alphabetically)
|
||||
|
||||
### [Bedag Informatik AG](https://www.bedag.ch/)
|
||||

|
||||
|
||||
### [Fastweb](https://www.fastweb.it/)
|
||||

|
||||
|
||||
### [Klarrio](https://klarrio.com/)
|
||||

|
||||
|
||||
### [PITS Global Data Recovery Services](https://www.pitsdatarecovery.net)
|
||||

|
||||
|
||||
### [Politecnico di Torino](https://www.polito.it/)
|
||||

|
||||
|
||||
### [Reevo](https://www.reevo.it/)
|
||||

|
||||
|
||||
### [University of Torino](https://www.unito.it)
|
||||

|
||||
|
||||
### [Velocity](https://velocity.tech/)
|
||||

|
||||
|
||||
### [Wargaming.net](https://www.wargaming.net/)
|
||||

|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
Capsule follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement by contacting
|
||||
one of the [maintainers](https://raw.githubusercontent.com/clastix/capsule/master/.github/maintainers.yaml).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md) and is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
@@ -1,58 +0,0 @@
|
||||
# Contributing
|
||||
|
||||
All contributions are welcome! If you find a bug or have a feature request, please open an issue or submit a pull request.
|
||||
|
||||
|
||||
## Guidelines
|
||||
|
||||
|
||||
## Pull Requests
|
||||
|
||||
|
||||
## Commits
|
||||
|
||||
Commit messages should indicate the change and it's impact. The general format for commit messages is the following:
|
||||
|
||||
feat(ui): Add `Button` component
|
||||
^ ^ ^
|
||||
| | |__ Subject
|
||||
| |_______ Scope
|
||||
|____________ Type
|
||||
|
||||
The commits are checked on pull-request. If the commit message does not follow the format, the workflow will fail. See the [Types](#types) and [Scopes](#scopes) sections for more information.
|
||||
|
||||
## Types
|
||||
|
||||
The following types are allowed for commits and pull requests:
|
||||
|
||||
* `ci` or `build`: changes to buillding process/workflows
|
||||
* `docs`: changes to documentation
|
||||
* `feat`: new features
|
||||
* `fix`: bug fixes
|
||||
|
||||
## Scopes
|
||||
|
||||
The following types are allowed for commits and pull requests:
|
||||
|
||||
* `all`: changes that affect all components
|
||||
* `chart`: changes to the Helm chart
|
||||
* `operator`: changes to the operator
|
||||
* `docs`: changes to the documentation
|
||||
* `website`: changes to the website
|
||||
* `ci`: changes to the CI/CD workflows
|
||||
* `build`: changes to the build process
|
||||
* `test`: changes to the testing process
|
||||
* `release`: changes to the release process
|
||||
* `deps`: dependency updates
|
||||
|
||||
### Sign-Off
|
||||
|
||||
Developer Certificate of Origin (DCO) Sign off
|
||||
For contributors to certify that they wrote or otherwise have the right to submit the code they are contributing to the project, we are requiring everyone to acknowledge this by signing their work which indicates you agree to the DCO found here.
|
||||
|
||||
To sign your work, just add a line like this at the end of your commit message:
|
||||
|
||||
Signed-off-by: Random J Developer <random@developer.example.org>
|
||||
This can easily be done with the -s command line option to append this automatically to your commit message.
|
||||
|
||||
git commit -s -m 'This is my commit message'
|
||||
18
Dockerfile
18
Dockerfile
@@ -1,13 +1,5 @@
|
||||
# Build the manager binary
|
||||
FROM golang:1.19.10 as builder
|
||||
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
COPY go.mod go.mod
|
||||
COPY go.sum go.sum
|
||||
# cache deps before building and copying source so that we don't need to re-download as much
|
||||
# and so that source changes don't invalidate our downloaded layer
|
||||
RUN go mod download
|
||||
FROM golang:1.18 as builder
|
||||
|
||||
ARG TARGETARCH
|
||||
ARG GIT_HEAD_COMMIT
|
||||
@@ -17,6 +9,14 @@ ARG GIT_MODIFIED
|
||||
ARG GIT_REPO
|
||||
ARG BUILD_DATE
|
||||
|
||||
WORKDIR /workspace
|
||||
# Copy the Go Modules manifests
|
||||
COPY go.mod go.mod
|
||||
COPY go.sum go.sum
|
||||
# cache deps before building and copying source so that we don't need to re-download as much
|
||||
# and so that source changes don't invalidate our downloaded layer
|
||||
RUN go mod download
|
||||
|
||||
# Copy the go source
|
||||
COPY main.go main.go
|
||||
COPY version.go version.go
|
||||
|
||||
251
Makefile
251
Makefile
@@ -1,23 +1,8 @@
|
||||
# Version
|
||||
GIT_HEAD_COMMIT ?= $(shell git rev-parse --short HEAD)
|
||||
VERSION ?= $(shell git describe --abbrev=0 --tags --match "v*")
|
||||
ifndef VERSION
|
||||
VERSION = $(GIT_HEAD_COMMIT)
|
||||
endif
|
||||
|
||||
# Defaults
|
||||
REGISTRY ?= ghcr.io
|
||||
REPOSITORY ?= projectcapsule/capsule
|
||||
GIT_TAG_COMMIT ?= $(shell git rev-parse --short $(VERSION))
|
||||
GIT_MODIFIED_1 ?= $(shell git diff $(GIT_HEAD_COMMIT) $(GIT_TAG_COMMIT) --quiet && echo "" || echo ".dev")
|
||||
GIT_MODIFIED_2 ?= $(shell git diff --quiet && echo "" || echo ".dirty")
|
||||
GIT_MODIFIED ?= $(shell echo "$(GIT_MODIFIED_1)$(GIT_MODIFIED_2)")
|
||||
GIT_REPO ?= $(shell git config --get remote.origin.url)
|
||||
BUILD_DATE ?= $(shell git log -1 --format="%at" | xargs -I{} sh -c 'if [ "$(shell uname)" = "Darwin" ]; then date -r {} +%Y-%m-%dT%H:%M:%S; else date -d @{} +%Y-%m-%dT%H:%M:%S; fi')
|
||||
IMG_BASE ?= $(REPOSITORY)
|
||||
IMG ?= $(IMG_BASE):$(VERSION)
|
||||
CAPSULE_IMG ?= $(REGISTRY)/$(IMG_BASE)
|
||||
# Current Operator version
|
||||
VERSION ?= $$(git describe --abbrev=0 --tags --match "v*")
|
||||
|
||||
# Default bundle image tag
|
||||
BUNDLE_IMG ?= clastix/capsule:$(VERSION)-bundle
|
||||
# Options for 'bundle-build'
|
||||
ifneq ($(origin CHANNELS), undefined)
|
||||
BUNDLE_CHANNELS := --channels=$(CHANNELS)
|
||||
@@ -27,6 +12,11 @@ BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
|
||||
endif
|
||||
BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
|
||||
|
||||
# Image URL to use all building/pushing image targets
|
||||
IMG ?= clastix/capsule:$(VERSION)
|
||||
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
|
||||
CRD_OPTIONS ?= "crd:preserveUnknownFields=false"
|
||||
|
||||
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
|
||||
ifeq (,$(shell go env GOBIN))
|
||||
GOBIN=$(shell go env GOPATH)/bin
|
||||
@@ -34,16 +24,20 @@ else
|
||||
GOBIN=$(shell go env GOBIN)
|
||||
endif
|
||||
|
||||
# Get information about git current status
|
||||
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")
|
||||
GIT_MODIFIED ?= $$(echo "$(GIT_MODIFIED_1)$(GIT_MODIFIED_2)")
|
||||
GIT_REPO ?= $$(git config --get remote.origin.url)
|
||||
BUILD_DATE ?= $$(git log -1 --format="%at" | xargs -I{} date -d @{} +%Y-%m-%dT%H:%M:%S)
|
||||
|
||||
all: manager
|
||||
|
||||
# Run tests
|
||||
.PHONY: test
|
||||
test: test-clean generate manifests test-clean
|
||||
@GO111MODULE=on go test -v ./... -coverprofile coverage.out
|
||||
|
||||
.PHONY: test-clean
|
||||
test-clean: ## Clean tests cache
|
||||
@go clean -testcache
|
||||
test: generate manifests
|
||||
go test ./... -coverprofile cover.out
|
||||
|
||||
# Build manager binary
|
||||
manager: generate golint
|
||||
@@ -55,7 +49,7 @@ run: generate manifests
|
||||
|
||||
# Creates the single file to install Capsule without any external dependency
|
||||
installer: manifests kustomize
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${CAPSULE_IMG}
|
||||
cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
|
||||
$(KUSTOMIZE) build config/default > config/install.yaml
|
||||
|
||||
# Install CRDs into a cluster
|
||||
@@ -78,39 +72,12 @@ remove: installer
|
||||
|
||||
# Generate manifests e.g. CRD, RBAC etc.
|
||||
manifests: controller-gen
|
||||
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
|
||||
# Generate code
|
||||
generate: controller-gen
|
||||
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
|
||||
|
||||
apidoc: apidocs-gen
|
||||
$(APIDOCS_GEN) crdoc --resources config/crd/bases --output docs/content/general/crds-apis.md --template docs/template/reference-cr.tmpl
|
||||
|
||||
# Helm
|
||||
SRC_ROOT = $(shell git rev-parse --show-toplevel)
|
||||
|
||||
helm-docs: HELMDOCS_VERSION := v1.11.0
|
||||
helm-docs: docker
|
||||
@docker run -v "$(SRC_ROOT):/helm-docs" jnorwood/helm-docs:$(HELMDOCS_VERSION) --chart-search-root /helm-docs
|
||||
|
||||
helm-lint: CT_VERSION := v3.3.1
|
||||
helm-lint: docker
|
||||
@docker run -v "$(SRC_ROOT):/workdir" --entrypoint /bin/sh quay.io/helmpack/chart-testing:$(CT_VERSION) -c "cd /workdir; ct lint --config .github/configs/ct.yaml --lint-conf .github/configs/lintconf.yaml --all --debug"
|
||||
|
||||
helm-test: kind ct ko-build-all
|
||||
@kind create cluster --wait=60s --name capsule-charts
|
||||
@kind load docker-image --name capsule-charts $(LOCAL_CAPSULE_IMG)
|
||||
@kubectl create ns capsule-system
|
||||
@ct install --config $(SRC_ROOT)/.github/configs/ct.yaml --namespace=capsule-system --all --debug
|
||||
@kind delete cluster --name capsule-charts
|
||||
|
||||
docker:
|
||||
@hash docker 2>/dev/null || {\
|
||||
echo "You need docker" &&\
|
||||
exit 1;\
|
||||
}
|
||||
|
||||
# Setup development env
|
||||
# Usage:
|
||||
# LAPTOP_HOST_IP=<YOUR_LAPTOP_IP> make dev-setup
|
||||
@@ -148,10 +115,7 @@ dev-setup:
|
||||
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}/defaults\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/1/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/defaults\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/2/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/defaults\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/3/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/namespace-owner-reference\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
{'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="[\
|
||||
@@ -159,117 +123,37 @@ dev-setup:
|
||||
{'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}/nodes\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/5/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/pods\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/6/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/persistentvolumeclaims\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/7/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/services\",'caBundle':\"$${CA_BUNDLE}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/8/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/tenants\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
]" && \
|
||||
kubectl patch crd tenants.capsule.clastix.io \
|
||||
--type='json' -p="[\
|
||||
{'op': 'replace', 'path': '/spec/conversion/webhook/clientConfig', 'value':{'url': \"$${WEBHOOK_URL}\", 'caBundle': \"$${CA_BUNDLE}\"}}\
|
||||
]" && \
|
||||
kubectl patch crd capsuleconfigurations.capsule.clastix.io \
|
||||
--type='json' -p="[\
|
||||
{'op': 'replace', 'path': '/spec/conversion/webhook/clientConfig', 'value':{'url': \"$${WEBHOOK_URL}\", '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}\"}},\
|
||||
{'op': 'replace', 'path': '/webhooks/8/clientConfig', 'value':{'url':\"$${WEBHOOK_URL}/nodes\",'caBundle':\"$${CA_BUNDLE}\"}}\
|
||||
]";
|
||||
|
||||
# Build the docker image
|
||||
docker-build: test
|
||||
docker build . -t ${IMG} --build-arg GIT_HEAD_COMMIT=$(GIT_HEAD_COMMIT) \
|
||||
--build-arg GIT_TAG_COMMIT=$(GIT_TAG_COMMIT) \
|
||||
--build-arg GIT_MODIFIED=$(GIT_MODIFIED) \
|
||||
--build-arg GIT_REPO=$(GIT_REPO) \
|
||||
--build-arg GIT_LAST_TAG=$(VERSION) \
|
||||
--build-arg BUILD_DATE=$(BUILD_DATE)
|
||||
|
||||
####################
|
||||
# -- Docker
|
||||
####################
|
||||
# Push the docker image
|
||||
docker-push:
|
||||
docker push ${IMG}
|
||||
|
||||
KOCACHE ?= /tmp/ko-cache
|
||||
KO_REGISTRY := ko.local
|
||||
KO_TAGS ?= "latest"
|
||||
ifdef VERSION
|
||||
KO_TAGS := $(KO_TAGS),$(VERSION)
|
||||
endif
|
||||
|
||||
LD_FLAGS := "-X main.Version=$(VERSION) \
|
||||
-X main.GitCommit=$(GIT_HEAD_COMMIT) \
|
||||
-X main.GitTag=$(VERSION) \
|
||||
-X main.GitTreeState=$(GIT_MODIFIED) \
|
||||
-X main.BuildDate=$(BUILD_DATE) \
|
||||
-X main.GitRepo=$(GIT_REPO)"
|
||||
|
||||
# Docker Image Build
|
||||
# ------------------
|
||||
|
||||
.PHONY: ko-build-capsule
|
||||
LOCAL_CAPSULE_IMG_BASE := github.com/$(REPOSITORY)
|
||||
LOCAL_CAPSULE_IMG := $(KO_REGISTRY)/$(LOCAL_CAPSULE_IMG_BASE)
|
||||
ko-build-capsule: ko
|
||||
@echo Building Capsule $(KO_TAGS) >&2
|
||||
@LD_FLAGS=$(LD_FLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(KO_REGISTRY) \
|
||||
$(KO) build ./ --preserve-import-paths --tags=$(KO_TAGS) --push=false
|
||||
|
||||
.PHONY: ko-build-all
|
||||
ko-build-all: ko-build-capsule
|
||||
|
||||
# Docker Image Publish
|
||||
# ------------------
|
||||
|
||||
REGISTRY_PASSWORD ?= dummy
|
||||
REGISTRY_USERNAME ?= dummy
|
||||
|
||||
.PHONY: ko-login
|
||||
ko-login: ko
|
||||
@$(KO) login $(REGISTRY) --username $(REGISTRY_USERNAME) --password $(REGISTRY_PASSWORD)
|
||||
|
||||
.PHONY: ko-publish-capsule
|
||||
ko-publish-capsule: ko-login ## Build and publish kyvernopre image (with ko)
|
||||
@LD_FLAGS=$(LD_FLAGS) KOCACHE=$(KOCACHE) KO_DOCKER_REPO=$(CAPSULE_IMG) \
|
||||
$(KO) build ./ --bare --tags=$(KO_TAGS)
|
||||
|
||||
.PHONY: ko-publish-all
|
||||
ko-publish-all: ko-publish-capsule
|
||||
|
||||
####################
|
||||
# -- Binaries
|
||||
####################
|
||||
|
||||
CONTROLLER_GEN := $(shell pwd)/bin/controller-gen
|
||||
CONTROLLER_GEN_VERSION := v0.10.0
|
||||
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
|
||||
controller-gen: ## Download controller-gen locally if necessary.
|
||||
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_GEN_VERSION))
|
||||
$(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.5.0)
|
||||
|
||||
APIDOCS_GEN := $(shell pwd)/bin/crdoc
|
||||
APIDOCS_GEN_VERSION := latest
|
||||
apidocs-gen: ## Download crdoc locally if necessary.
|
||||
$(call go-install-tool,$(APIDOCS_GEN),fybrik.io/crdoc@$(APIDOCS_GEN_VERSION))
|
||||
|
||||
GINKGO := $(shell pwd)/bin/ginkgo
|
||||
GINGKO_VERSION := v2.9.5
|
||||
GINKGO = $(shell pwd)/bin/ginkgo
|
||||
ginkgo: ## Download ginkgo locally if necessary.
|
||||
$(call go-install-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo@$(GINGKO_VERSION))
|
||||
$(call go-install-tool,$(KUSTOMIZE),github.com/onsi/ginkgo/ginkgo@v1.16.5)
|
||||
|
||||
CT := $(shell pwd)/bin/ct
|
||||
CT_VERSION := v3.7.1
|
||||
ct: ## Download ct locally if necessary.
|
||||
$(call go-install-tool,$(CT),github.com/helm/chart-testing/v3/ct@$(CT_VERSION))
|
||||
|
||||
KIND := $(shell pwd)/bin/kind
|
||||
KIND_VERSION := v0.17.0
|
||||
kind: ## Download kind locally if necessary.
|
||||
$(call go-install-tool,$(KIND),sigs.k8s.io/kind/cmd/kind@$(KIND_VERSION))
|
||||
|
||||
KUSTOMIZE := $(shell pwd)/bin/kustomize
|
||||
KUSTOMIZE_VERSION := 3.8.7
|
||||
KUSTOMIZE = $(shell pwd)/bin/kustomize
|
||||
kustomize: ## Download kustomize locally if necessary.
|
||||
$(call install-kustomize,$(KUSTOMIZE),$(KUSTOMIZE_VERSION))
|
||||
|
||||
KO = $(shell pwd)/bin/ko
|
||||
KO_VERSION = v0.14.1
|
||||
ko:
|
||||
$(call go-install-tool,$(KO),github.com/google/ko@v0.14.1)
|
||||
|
||||
####################
|
||||
# -- Helpers
|
||||
####################
|
||||
pull-upstream:
|
||||
git remote add upstream https://github.com/capsuleproject/capsule.git
|
||||
git fetch --all && git pull upstream
|
||||
$(call install-kustomize,$(KUSTOMIZE),3.8.7)
|
||||
|
||||
define install-kustomize
|
||||
@[ -f $(1) ] || { \
|
||||
@@ -286,6 +170,7 @@ PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||
define go-install-tool
|
||||
@[ -f $(1) ] || { \
|
||||
set -e ;\
|
||||
echo "Installing $(2)" ;\
|
||||
GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\
|
||||
}
|
||||
endef
|
||||
@@ -296,32 +181,26 @@ bundle: manifests
|
||||
kustomize build config/manifests | operator-sdk generate bundle -q --overwrite --version $(VERSION) $(BUNDLE_METADATA_OPTS)
|
||||
operator-sdk bundle validate ./bundle
|
||||
|
||||
# Build the bundle image.
|
||||
bundle-build:
|
||||
docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) .
|
||||
|
||||
# Sorting imports
|
||||
.PHONY: goimports
|
||||
goimports:
|
||||
goimports -w -l -local "github.com/projectcapsule/capsule" .
|
||||
|
||||
GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint
|
||||
golangci-lint: ## Download golangci-lint locally if necessary.
|
||||
$(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/cmd/golangci-lint@v1.51.2)
|
||||
goimports -w -l -local "github.com/clastix/capsule" .
|
||||
|
||||
# Linting code as PR is expecting
|
||||
.PHONY: golint
|
||||
golint: golangci-lint
|
||||
$(GOLANGCI_LINT) run -c .golangci.yml
|
||||
golint:
|
||||
golangci-lint run -c .golangci.yml
|
||||
|
||||
# Running e2e tests in a KinD instance
|
||||
.PHONY: e2e
|
||||
e2e/%: ginkgo
|
||||
$(MAKE) e2e-build/$* && $(MAKE) e2e-exec && $(MAKE) e2e-destroy
|
||||
|
||||
e2e-build/%:
|
||||
kind create cluster --wait=60s --name capsule --image=kindest/node:$*
|
||||
make e2e-load-image
|
||||
make e2e-install
|
||||
|
||||
.PHONY: e2e-install
|
||||
e2e-install:
|
||||
kind create cluster --name capsule --image=kindest/node:$*
|
||||
make docker-build
|
||||
kind load docker-image --nodes capsule-control-plane --name capsule $(IMG)
|
||||
helm upgrade \
|
||||
--debug \
|
||||
--install \
|
||||
@@ -330,27 +209,9 @@ e2e-install:
|
||||
--set 'manager.image.pullPolicy=Never' \
|
||||
--set 'manager.resources=null'\
|
||||
--set "manager.image.tag=$(VERSION)" \
|
||||
--set 'manager.image.registry=$(KO_REGISTRY)' \
|
||||
--set 'manager.image.repository=$(LOCAL_CAPSULE_IMG_BASE)' \
|
||||
--set 'manager.livenessProbe.failureThreshold=10' \
|
||||
--set 'manager.readinessProbe.failureThreshold=10' \
|
||||
--set 'podSecurityContext.seccompProfile=null' \
|
||||
capsule \
|
||||
./charts/capsule
|
||||
|
||||
.PHONY: e2e-load-image
|
||||
e2e-load-image: ko-build-all
|
||||
kind load docker-image --nodes capsule-control-plane --name capsule $(LOCAL_CAPSULE_IMG):$(VERSION)
|
||||
|
||||
.PHONY: e2e-exec
|
||||
e2e-exec: ginkgo
|
||||
$(GINKGO) -v -tags e2e ./e2e
|
||||
|
||||
.PHONY: e2e-destroy
|
||||
e2e-destroy:
|
||||
kind delete cluster --name capsule
|
||||
|
||||
SPELL_CHECKER = npx spellchecker-cli
|
||||
docs-lint:
|
||||
cd docs/content && $(SPELL_CHECKER) -f "*.md" "*/*.md" -d dictionary.txt
|
||||
|
||||
|
||||
41
PROJECT
41
PROJECT
@@ -5,62 +5,35 @@ plugins:
|
||||
manifests.sdk.operatorframework.io/v2: {}
|
||||
scorecard.sdk.operatorframework.io/v2: {}
|
||||
projectName: capsule
|
||||
repo: github.com/projectcapsule/capsule
|
||||
repo: github.com/clastix/capsule
|
||||
resources:
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: false
|
||||
controller: true
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: Tenant
|
||||
path: github.com/projectcapsule/capsule/api/v1alpha1
|
||||
path: github.com/clastix/capsule/api/v1alpha1
|
||||
version: v1alpha1
|
||||
webhooks:
|
||||
conversion: true
|
||||
webhookVersion: v1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: false
|
||||
controller: true
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: CapsuleConfiguration
|
||||
path: github.com/projectcapsule/capsule/api/v1alpha1
|
||||
path: github.com/clastix/capsule/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: false
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: Tenant
|
||||
path: github.com/projectcapsule/capsule/api/v1beta1
|
||||
path: github.com/clastix/capsule/api/v1beta1
|
||||
version: v1beta1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: Tenant
|
||||
path: github.com/projectcapsule/capsule/api/v1beta2
|
||||
version: v1beta2
|
||||
- api:
|
||||
crdVersion: v1
|
||||
controller: true
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: CapsuleConfiguration
|
||||
path: github.com/projectcapsule/capsule/api/v1beta2
|
||||
version: v1beta2
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: TenantResource
|
||||
path: github.com/projectcapsule/capsule/api/v1beta2
|
||||
version: v1beta2
|
||||
- api:
|
||||
crdVersion: v1
|
||||
domain: clastix.io
|
||||
group: capsule
|
||||
kind: GlobalTenantResource
|
||||
path: github.com/projectcapsule/capsule/api/v1beta2
|
||||
version: v1beta2
|
||||
version: "3"
|
||||
|
||||
33
README.md
33
README.md
@@ -1,20 +1,13 @@
|
||||
|
||||
<p align="left">
|
||||
<img src="https://github.com/projectcapsule/capsule/actions/workflows/ci.yml/badge.svg"/>
|
||||
<img src="https://img.shields.io/github/license/clastix/capsule"/>
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/clastix/capsule"/>
|
||||
<a href="https://github.com/projectcapsule/capsule/releases">
|
||||
<a href="https://github.com/clastix/capsule/releases">
|
||||
<img src="https://img.shields.io/github/v/release/clastix/capsule"/>
|
||||
</a>
|
||||
<a href="https://charmhub.io/capsule-k8s">
|
||||
<img src="https://charmhub.io/capsule-k8s/badge.svg"/>
|
||||
</a>
|
||||
<a href="https://www.bestpractices.dev/projects/5601">
|
||||
<img src="https://www.bestpractices.dev/projects/5601/badge"/>
|
||||
</a>
|
||||
<a href="https://api.securityscorecards.dev/projects/github.com/projectcapsule/capsule/badge">
|
||||
<img src="https://api.securityscorecards.dev/projects/github.com/projectcapsule/capsule/badge"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
@@ -79,27 +72,17 @@ Capsule is Open Source with Apache 2 license and any contribution is welcome.
|
||||
|
||||
## Chart Development
|
||||
|
||||
### Chart Linting
|
||||
The documentation for each chart is done with [helm-docs](https://github.com/norwoodj/helm-docs). This way we can ensure that values are consistent with the chart documentation.
|
||||
|
||||
The chart is linted with [ct](https://github.com/helm/chart-testing). You can run the linter locally with this command:
|
||||
We have a script on the repository which will execute the helm-docs docker container, so that you don't have to worry about downloading the binary etc. Simply execute the script (Bash compatible):
|
||||
|
||||
```
|
||||
make helm-lint
|
||||
```
|
||||
|
||||
### Chart Documentation
|
||||
|
||||
The documentation for each chart is done with [helm-docs](https://github.com/norwoodj/helm-docs). This way we can ensure that values are consistent with the chart documentation. Run this anytime you make changes to a `values.yaml` file:
|
||||
|
||||
```
|
||||
make helm-docs
|
||||
bash scripts/helm-docs.sh
|
||||
```
|
||||
|
||||
## Community
|
||||
|
||||
Join the community, share and learn from it. You can find all the resources to how to contribute code and docs, connect with people in the [community repository](https://github.com/projectcapsule/capsule-community).
|
||||
|
||||
Please read the [code of conduct](CODE_OF_CONDUCT.md).
|
||||
Join the community, share and learn from it. You can find all the resources to how to contribute code and docs, connect with people in the [community repository](https://github.com/clastix/capsule-community).
|
||||
|
||||
## Adopters
|
||||
|
||||
@@ -113,10 +96,6 @@ You can find how the Capsule project is governed [here](https://capsule.clastix.
|
||||
|
||||
Please, refer to the maintainers file available [here](.github/maintainers.yaml).
|
||||
|
||||
## Release process
|
||||
|
||||
Please, refer to the [documentation page](https://capsule.clastix.io/docs/contributing/release).
|
||||
|
||||
# FAQ
|
||||
|
||||
- Q. How to pronounce Capsule?
|
||||
@@ -125,7 +104,7 @@ Please, refer to the [documentation page](https://capsule.clastix.io/docs/contri
|
||||
|
||||
- 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/projectcapsule/capsule/releases) page for a detailed list of available versions.
|
||||
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 Kubernetes XYZ distribution?
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
type AdditionalMetadata struct {
|
||||
Labels map[string]string `json:"additionalLabels,omitempty"`
|
||||
Annotations map[string]string `json:"additionalAnnotations,omitempty"`
|
||||
type AdditionalMetadataSpec struct {
|
||||
AdditionalLabels map[string]string `json:"additionalLabels,omitempty"`
|
||||
AdditionalAnnotations map[string]string `json:"additionalAnnotations,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
package v1alpha1
|
||||
|
||||
import rbacv1 "k8s.io/api/rbac/v1"
|
||||
|
||||
37
api/v1alpha1/allowed_list.go
Normal file
37
api/v1alpha1/allowed_list.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
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
|
||||
}
|
||||
73
api/v1alpha1/allowed_list_test.go
Normal file
73
api/v1alpha1/allowed_list_test.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,3 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
const (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -31,8 +31,6 @@ type CapsuleConfiguration struct {
|
||||
Spec CapsuleConfigurationSpec `json:"spec,omitempty"`
|
||||
}
|
||||
|
||||
func (in *CapsuleConfiguration) Hub() {}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// CapsuleConfigurationList contains a list of CapsuleConfiguration.
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
func (in *CapsuleConfiguration) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
||||
certData, _ := os.ReadFile("/tmp/k8s-webhook-server/serving-certs/tls.crt")
|
||||
if len(certData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ctrl.NewWebhookManagedBy(mgr).
|
||||
For(in).
|
||||
Complete()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -13,8 +13,7 @@ import (
|
||||
"k8s.io/utils/pointer"
|
||||
"sigs.k8s.io/controller-runtime/pkg/conversion"
|
||||
|
||||
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -49,7 +48,7 @@ const (
|
||||
ingressHostnameCollisionScope = "ingress.capsule.clastix.io/hostname-collision-scope"
|
||||
)
|
||||
|
||||
func (in *Tenant) convertV1Alpha1OwnerToV1Beta1() capsulev1beta1.OwnerListSpec {
|
||||
func (t *Tenant) convertV1Alpha1OwnerToV1Beta1() capsulev1beta1.OwnerListSpec {
|
||||
serviceKindToAnnotationMap := map[capsulev1beta1.ProxyServiceKind][]string{
|
||||
capsulev1beta1.NodesProxy: {enableNodeListingAnnotation, enableNodeUpdateAnnotation, enableNodeDeletionAnnotation},
|
||||
capsulev1beta1.StorageClassesProxy: {enableStorageClassListingAnnotation, enableStorageClassUpdateAnnotation, enableStorageClassDeletionAnnotation},
|
||||
@@ -76,7 +75,7 @@ func (in *Tenant) convertV1Alpha1OwnerToV1Beta1() capsulev1beta1.OwnerListSpec {
|
||||
ownerServiceAccountAnnotation: capsulev1beta1.ServiceAccountOwner,
|
||||
}
|
||||
|
||||
annotations := in.GetAnnotations()
|
||||
annotations := t.GetAnnotations()
|
||||
|
||||
operations := make(map[string]map[capsulev1beta1.ProxyServiceKind][]capsulev1beta1.ProxyOperation)
|
||||
|
||||
@@ -112,9 +111,9 @@ func (in *Tenant) convertV1Alpha1OwnerToV1Beta1() capsulev1beta1.OwnerListSpec {
|
||||
}
|
||||
|
||||
owners = append(owners, capsulev1beta1.OwnerSpec{
|
||||
Kind: capsulev1beta1.OwnerKind(in.Spec.Owner.Kind),
|
||||
Name: in.Spec.Owner.Name,
|
||||
ProxyOperations: getProxySettingsForOwner(in.Spec.Owner.Name),
|
||||
Kind: capsulev1beta1.OwnerKind(t.Spec.Owner.Kind),
|
||||
Name: t.Spec.Owner.Name,
|
||||
ProxyOperations: getProxySettingsForOwner(t.Spec.Owner.Name),
|
||||
})
|
||||
|
||||
for ownerAnnotation, ownerKind := range annotationToOwnerKindMap {
|
||||
@@ -133,135 +132,151 @@ func (in *Tenant) convertV1Alpha1OwnerToV1Beta1() capsulev1beta1.OwnerListSpec {
|
||||
return owners
|
||||
}
|
||||
|
||||
//nolint:gocognit,gocyclo,cyclop,maintidx
|
||||
func (in *Tenant) ConvertTo(dstRaw conversion.Hub) error {
|
||||
// nolint:gocognit,gocyclo,cyclop,maintidx
|
||||
func (t *Tenant) ConvertTo(dstRaw conversion.Hub) error {
|
||||
dst, ok := dstRaw.(*capsulev1beta1.Tenant)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected type *capsulev1beta1.Tenant, got %T", dst)
|
||||
}
|
||||
|
||||
annotations := in.GetAnnotations()
|
||||
annotations := t.GetAnnotations()
|
||||
|
||||
// ObjectMeta
|
||||
dst.ObjectMeta = in.ObjectMeta
|
||||
dst.ObjectMeta = t.ObjectMeta
|
||||
|
||||
// Spec
|
||||
if in.Spec.NamespaceQuota != nil {
|
||||
if t.Spec.NamespaceQuota != nil {
|
||||
if dst.Spec.NamespaceOptions == nil {
|
||||
dst.Spec.NamespaceOptions = &capsulev1beta1.NamespaceOptions{}
|
||||
}
|
||||
|
||||
dst.Spec.NamespaceOptions.Quota = in.Spec.NamespaceQuota
|
||||
dst.Spec.NamespaceOptions.Quota = t.Spec.NamespaceQuota
|
||||
}
|
||||
|
||||
dst.Spec.NodeSelector = in.Spec.NodeSelector
|
||||
dst.Spec.NodeSelector = t.Spec.NodeSelector
|
||||
|
||||
dst.Spec.Owners = in.convertV1Alpha1OwnerToV1Beta1()
|
||||
dst.Spec.Owners = t.convertV1Alpha1OwnerToV1Beta1()
|
||||
|
||||
if in.Spec.NamespacesMetadata != nil {
|
||||
if t.Spec.NamespacesMetadata != nil {
|
||||
if dst.Spec.NamespaceOptions == nil {
|
||||
dst.Spec.NamespaceOptions = &capsulev1beta1.NamespaceOptions{}
|
||||
}
|
||||
|
||||
dst.Spec.NamespaceOptions.AdditionalMetadata = &api.AdditionalMetadataSpec{
|
||||
Labels: in.Spec.NamespacesMetadata.Labels,
|
||||
Annotations: in.Spec.NamespacesMetadata.Annotations,
|
||||
dst.Spec.NamespaceOptions.AdditionalMetadata = &capsulev1beta1.AdditionalMetadataSpec{
|
||||
Labels: t.Spec.NamespacesMetadata.AdditionalLabels,
|
||||
Annotations: t.Spec.NamespacesMetadata.AdditionalAnnotations,
|
||||
}
|
||||
}
|
||||
|
||||
if in.Spec.ServicesMetadata != nil {
|
||||
if t.Spec.ServicesMetadata != nil {
|
||||
if dst.Spec.ServiceOptions == nil {
|
||||
dst.Spec.ServiceOptions = &api.ServiceOptions{}
|
||||
}
|
||||
|
||||
dst.Spec.ServiceOptions.AdditionalMetadata = &api.AdditionalMetadataSpec{
|
||||
Labels: in.Spec.ServicesMetadata.Labels,
|
||||
Annotations: in.Spec.ServicesMetadata.Annotations,
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{
|
||||
AdditionalMetadata: &capsulev1beta1.AdditionalMetadataSpec{
|
||||
Labels: t.Spec.ServicesMetadata.AdditionalLabels,
|
||||
Annotations: t.Spec.ServicesMetadata.AdditionalAnnotations,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if in.Spec.StorageClasses != nil {
|
||||
dst.Spec.StorageClasses = in.Spec.StorageClasses
|
||||
if t.Spec.StorageClasses != nil {
|
||||
dst.Spec.StorageClasses = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.StorageClasses.Exact,
|
||||
Regex: t.Spec.StorageClasses.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
if v, annotationOk := in.Annotations[ingressHostnameCollisionScope]; annotationOk {
|
||||
if v, annotationOk := t.Annotations[ingressHostnameCollisionScope]; annotationOk {
|
||||
switch v {
|
||||
case string(api.HostnameCollisionScopeCluster), string(api.HostnameCollisionScopeTenant), string(api.HostnameCollisionScopeNamespace):
|
||||
dst.Spec.IngressOptions.HostnameCollisionScope = api.HostnameCollisionScope(v)
|
||||
case string(capsulev1beta1.HostnameCollisionScopeCluster), string(capsulev1beta1.HostnameCollisionScopeTenant), string(capsulev1beta1.HostnameCollisionScopeNamespace):
|
||||
dst.Spec.IngressOptions.HostnameCollisionScope = capsulev1beta1.HostnameCollisionScope(v)
|
||||
default:
|
||||
dst.Spec.IngressOptions.HostnameCollisionScope = api.HostnameCollisionScopeDisabled
|
||||
dst.Spec.IngressOptions.HostnameCollisionScope = capsulev1beta1.HostnameCollisionScopeDisabled
|
||||
}
|
||||
}
|
||||
|
||||
if in.Spec.IngressClasses != nil {
|
||||
dst.Spec.IngressOptions.AllowedClasses = &api.AllowedListSpec{
|
||||
Exact: in.Spec.IngressClasses.Exact,
|
||||
Regex: in.Spec.IngressClasses.Regex,
|
||||
if t.Spec.IngressClasses != nil {
|
||||
dst.Spec.IngressOptions.AllowedClasses = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.IngressClasses.Exact,
|
||||
Regex: t.Spec.IngressClasses.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
if in.Spec.IngressHostnames != nil {
|
||||
dst.Spec.IngressOptions.AllowedHostnames = &api.AllowedListSpec{
|
||||
Exact: in.Spec.IngressHostnames.Exact,
|
||||
Regex: in.Spec.IngressHostnames.Regex,
|
||||
if t.Spec.IngressHostnames != nil {
|
||||
dst.Spec.IngressOptions.AllowedHostnames = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.IngressHostnames.Exact,
|
||||
Regex: t.Spec.IngressHostnames.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
if in.Spec.ContainerRegistries != nil {
|
||||
dst.Spec.ContainerRegistries = &api.AllowedListSpec{
|
||||
Exact: in.Spec.ContainerRegistries.Exact,
|
||||
Regex: in.Spec.ContainerRegistries.Regex,
|
||||
if t.Spec.ContainerRegistries != nil {
|
||||
dst.Spec.ContainerRegistries = &capsulev1beta1.AllowedListSpec{
|
||||
Exact: t.Spec.ContainerRegistries.Exact,
|
||||
Regex: t.Spec.ContainerRegistries.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
if len(in.Spec.NetworkPolicies) > 0 {
|
||||
dst.Spec.NetworkPolicies = api.NetworkPolicySpec{
|
||||
Items: in.Spec.NetworkPolicies,
|
||||
if len(t.Spec.NetworkPolicies) > 0 {
|
||||
dst.Spec.NetworkPolicies = capsulev1beta1.NetworkPolicySpec{
|
||||
Items: t.Spec.NetworkPolicies,
|
||||
}
|
||||
}
|
||||
|
||||
if len(in.Spec.LimitRanges) > 0 {
|
||||
dst.Spec.LimitRanges = api.LimitRangesSpec{
|
||||
Items: in.Spec.LimitRanges,
|
||||
if len(t.Spec.LimitRanges) > 0 {
|
||||
dst.Spec.LimitRanges = capsulev1beta1.LimitRangesSpec{
|
||||
Items: t.Spec.LimitRanges,
|
||||
}
|
||||
}
|
||||
|
||||
if len(in.Spec.ResourceQuota) > 0 {
|
||||
dst.Spec.ResourceQuota = api.ResourceQuotaSpec{
|
||||
Scope: func() api.ResourceQuotaScope {
|
||||
if v, annotationOk := in.GetAnnotations()[resourceQuotaScopeAnnotation]; annotationOk {
|
||||
if len(t.Spec.ResourceQuota) > 0 {
|
||||
dst.Spec.ResourceQuota = capsulev1beta1.ResourceQuotaSpec{
|
||||
Scope: func() capsulev1beta1.ResourceQuotaScope {
|
||||
if v, annotationOk := t.GetAnnotations()[resourceQuotaScopeAnnotation]; annotationOk {
|
||||
switch v {
|
||||
case string(api.ResourceQuotaScopeNamespace):
|
||||
return api.ResourceQuotaScopeNamespace
|
||||
case string(api.ResourceQuotaScopeTenant):
|
||||
return api.ResourceQuotaScopeTenant
|
||||
case string(capsulev1beta1.ResourceQuotaScopeNamespace):
|
||||
return capsulev1beta1.ResourceQuotaScopeNamespace
|
||||
case string(capsulev1beta1.ResourceQuotaScopeTenant):
|
||||
return capsulev1beta1.ResourceQuotaScopeTenant
|
||||
}
|
||||
}
|
||||
|
||||
return api.ResourceQuotaScopeTenant
|
||||
return capsulev1beta1.ResourceQuotaScopeTenant
|
||||
}(),
|
||||
Items: in.Spec.ResourceQuota,
|
||||
Items: t.Spec.ResourceQuota,
|
||||
}
|
||||
}
|
||||
|
||||
dst.Spec.AdditionalRoleBindings = in.Spec.AdditionalRoleBindings
|
||||
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 in.Spec.ExternalServiceIPs != nil {
|
||||
if t.Spec.ExternalServiceIPs != nil {
|
||||
if dst.Spec.ServiceOptions == nil {
|
||||
dst.Spec.ServiceOptions = &api.ServiceOptions{}
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
|
||||
dst.Spec.ServiceOptions.ExternalServiceIPs = in.Spec.ExternalServiceIPs
|
||||
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, api.ImagePullPolicySpec(policy))
|
||||
dst.Spec.ImagePullPolicies = append(dst.Spec.ImagePullPolicies, capsulev1beta1.ImagePullPolicySpec(policy))
|
||||
}
|
||||
}
|
||||
|
||||
priorityClasses := api.AllowedListSpec{}
|
||||
priorityClasses := capsulev1beta1.AllowedListSpec{}
|
||||
|
||||
priorityClassAllowed, ok := annotations[podPriorityAllowedAnnotation]
|
||||
|
||||
@@ -283,59 +298,59 @@ func (in *Tenant) ConvertTo(dstRaw conversion.Hub) error {
|
||||
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, in.GetName()))
|
||||
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 = &api.ServiceOptions{}
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
|
||||
if dst.Spec.ServiceOptions.AllowedServices == nil {
|
||||
dst.Spec.ServiceOptions.AllowedServices = &api.AllowedServices{}
|
||||
dst.Spec.ServiceOptions.AllowedServices = &capsulev1beta1.AllowedServices{}
|
||||
}
|
||||
|
||||
dst.Spec.ServiceOptions.AllowedServices.NodePort = pointer.Bool(val)
|
||||
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, in.GetName()))
|
||||
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 = &api.ServiceOptions{}
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
|
||||
if dst.Spec.ServiceOptions.AllowedServices == nil {
|
||||
dst.Spec.ServiceOptions.AllowedServices = &api.AllowedServices{}
|
||||
dst.Spec.ServiceOptions.AllowedServices = &capsulev1beta1.AllowedServices{}
|
||||
}
|
||||
|
||||
dst.Spec.ServiceOptions.AllowedServices.ExternalName = pointer.Bool(val)
|
||||
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, in.GetName()))
|
||||
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 = &api.ServiceOptions{}
|
||||
dst.Spec.ServiceOptions = &capsulev1beta1.ServiceOptions{}
|
||||
}
|
||||
|
||||
if dst.Spec.ServiceOptions.AllowedServices == nil {
|
||||
dst.Spec.ServiceOptions.AllowedServices = &api.AllowedServices{}
|
||||
dst.Spec.ServiceOptions.AllowedServices = &capsulev1beta1.AllowedServices{}
|
||||
}
|
||||
|
||||
dst.Spec.ServiceOptions.AllowedServices.LoadBalancer = pointer.Bool(val)
|
||||
dst.Spec.ServiceOptions.AllowedServices.LoadBalancer = pointer.BoolPtr(val)
|
||||
}
|
||||
// Status
|
||||
dst.Status = capsulev1beta1.TenantStatus{
|
||||
Size: in.Status.Size,
|
||||
Namespaces: in.Status.Namespaces,
|
||||
Size: t.Status.Size,
|
||||
Namespaces: t.Status.Namespaces,
|
||||
}
|
||||
// Remove unneeded annotations
|
||||
delete(dst.ObjectMeta.Annotations, podAllowedImagePullPolicyAnnotation)
|
||||
@@ -365,8 +380,8 @@ func (in *Tenant) ConvertTo(dstRaw conversion.Hub) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:gocognit,gocyclo,cyclop
|
||||
func (in *Tenant) convertV1Beta1OwnerToV1Alpha1(src *capsulev1beta1.Tenant) {
|
||||
// nolint:gocognit,gocyclo,cyclop
|
||||
func (t *Tenant) convertV1Beta1OwnerToV1Alpha1(src *capsulev1beta1.Tenant) {
|
||||
ownersAnnotations := map[string][]string{
|
||||
ownerGroupsAnnotation: nil,
|
||||
ownerUsersAnnotation: nil,
|
||||
@@ -387,7 +402,7 @@ func (in *Tenant) convertV1Beta1OwnerToV1Alpha1(src *capsulev1beta1.Tenant) {
|
||||
|
||||
for i, owner := range src.Spec.Owners {
|
||||
if i == 0 {
|
||||
in.Spec.Owner = OwnerSpec{
|
||||
t.Spec.Owner = OwnerSpec{
|
||||
Name: owner.Name,
|
||||
Kind: Kind(owner.Kind),
|
||||
}
|
||||
@@ -454,89 +469,114 @@ func (in *Tenant) convertV1Beta1OwnerToV1Alpha1(src *capsulev1beta1.Tenant) {
|
||||
|
||||
for k, v := range ownersAnnotations {
|
||||
if len(v) > 0 {
|
||||
in.Annotations[k] = strings.Join(v, ",")
|
||||
t.Annotations[k] = strings.Join(v, ",")
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range proxyAnnotations {
|
||||
if len(v) > 0 {
|
||||
in.Annotations[k] = strings.Join(v, ",")
|
||||
t.Annotations[k] = strings.Join(v, ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:cyclop
|
||||
func (in *Tenant) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
// nolint:gocyclo,cyclop
|
||||
func (t *Tenant) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
src, ok := srcRaw.(*capsulev1beta1.Tenant)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected *capsulev1beta1.Tenant, got %T", srcRaw)
|
||||
}
|
||||
|
||||
// ObjectMeta
|
||||
in.ObjectMeta = src.ObjectMeta
|
||||
t.ObjectMeta = src.ObjectMeta
|
||||
|
||||
// Spec
|
||||
if src.Spec.NamespaceOptions != nil && src.Spec.NamespaceOptions.Quota != nil {
|
||||
in.Spec.NamespaceQuota = src.Spec.NamespaceOptions.Quota
|
||||
t.Spec.NamespaceQuota = src.Spec.NamespaceOptions.Quota
|
||||
}
|
||||
|
||||
in.Spec.NodeSelector = src.Spec.NodeSelector
|
||||
t.Spec.NodeSelector = src.Spec.NodeSelector
|
||||
|
||||
if in.Annotations == nil {
|
||||
in.Annotations = make(map[string]string)
|
||||
if t.Annotations == nil {
|
||||
t.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
in.convertV1Beta1OwnerToV1Alpha1(src)
|
||||
t.convertV1Beta1OwnerToV1Alpha1(src)
|
||||
|
||||
if src.Spec.NamespaceOptions != nil && src.Spec.NamespaceOptions.AdditionalMetadata != nil {
|
||||
in.Spec.NamespacesMetadata = &AdditionalMetadata{
|
||||
Labels: src.Spec.NamespaceOptions.AdditionalMetadata.Labels,
|
||||
Annotations: src.Spec.NamespaceOptions.AdditionalMetadata.Annotations,
|
||||
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 {
|
||||
in.Spec.ServicesMetadata = &AdditionalMetadata{
|
||||
Labels: src.Spec.ServiceOptions.AdditionalMetadata.Labels,
|
||||
Annotations: src.Spec.ServiceOptions.AdditionalMetadata.Annotations,
|
||||
t.Spec.ServicesMetadata = &AdditionalMetadataSpec{
|
||||
AdditionalLabels: src.Spec.ServiceOptions.AdditionalMetadata.Labels,
|
||||
AdditionalAnnotations: src.Spec.ServiceOptions.AdditionalMetadata.Annotations,
|
||||
}
|
||||
}
|
||||
|
||||
if src.Spec.StorageClasses != nil {
|
||||
in.Spec.StorageClasses = src.Spec.StorageClasses
|
||||
t.Spec.StorageClasses = &AllowedListSpec{
|
||||
Exact: src.Spec.StorageClasses.Exact,
|
||||
Regex: src.Spec.StorageClasses.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
in.Annotations[ingressHostnameCollisionScope] = string(src.Spec.IngressOptions.HostnameCollisionScope)
|
||||
t.Annotations[ingressHostnameCollisionScope] = string(src.Spec.IngressOptions.HostnameCollisionScope)
|
||||
|
||||
if src.Spec.IngressOptions.AllowedClasses != nil {
|
||||
in.Spec.IngressClasses = src.Spec.IngressOptions.AllowedClasses
|
||||
t.Spec.IngressClasses = &AllowedListSpec{
|
||||
Exact: src.Spec.IngressOptions.AllowedClasses.Exact,
|
||||
Regex: src.Spec.IngressOptions.AllowedClasses.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
if src.Spec.IngressOptions.AllowedHostnames != nil {
|
||||
in.Spec.IngressHostnames = src.Spec.IngressOptions.AllowedHostnames
|
||||
t.Spec.IngressHostnames = &AllowedListSpec{
|
||||
Exact: src.Spec.IngressOptions.AllowedHostnames.Exact,
|
||||
Regex: src.Spec.IngressOptions.AllowedHostnames.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
if src.Spec.ContainerRegistries != nil {
|
||||
in.Spec.ContainerRegistries = src.Spec.ContainerRegistries
|
||||
t.Spec.ContainerRegistries = &AllowedListSpec{
|
||||
Exact: src.Spec.ContainerRegistries.Exact,
|
||||
Regex: src.Spec.ContainerRegistries.Regex,
|
||||
}
|
||||
}
|
||||
|
||||
if len(src.Spec.NetworkPolicies.Items) > 0 {
|
||||
in.Spec.NetworkPolicies = src.Spec.NetworkPolicies.Items
|
||||
t.Spec.NetworkPolicies = src.Spec.NetworkPolicies.Items
|
||||
}
|
||||
|
||||
if len(src.Spec.LimitRanges.Items) > 0 {
|
||||
in.Spec.LimitRanges = src.Spec.LimitRanges.Items
|
||||
t.Spec.LimitRanges = src.Spec.LimitRanges.Items
|
||||
}
|
||||
|
||||
if len(src.Spec.ResourceQuota.Items) > 0 {
|
||||
in.Annotations[resourceQuotaScopeAnnotation] = string(src.Spec.ResourceQuota.Scope)
|
||||
in.Spec.ResourceQuota = src.Spec.ResourceQuota.Items
|
||||
t.Annotations[resourceQuotaScopeAnnotation] = string(src.Spec.ResourceQuota.Scope)
|
||||
t.Spec.ResourceQuota = src.Spec.ResourceQuota.Items
|
||||
}
|
||||
|
||||
in.Spec.AdditionalRoleBindings = src.Spec.AdditionalRoleBindings
|
||||
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 {
|
||||
in.Spec.ExternalServiceIPs = src.Spec.ServiceOptions.ExternalServiceIPs
|
||||
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 {
|
||||
@@ -546,35 +586,35 @@ func (in *Tenant) ConvertFrom(srcRaw conversion.Hub) error {
|
||||
pullPolicies = append(pullPolicies, string(policy))
|
||||
}
|
||||
|
||||
in.Annotations[podAllowedImagePullPolicyAnnotation] = strings.Join(pullPolicies, ",")
|
||||
t.Annotations[podAllowedImagePullPolicyAnnotation] = strings.Join(pullPolicies, ",")
|
||||
}
|
||||
|
||||
if src.Spec.PriorityClasses != nil {
|
||||
if len(src.Spec.PriorityClasses.Exact) != 0 {
|
||||
in.Annotations[podPriorityAllowedAnnotation] = strings.Join(src.Spec.PriorityClasses.Exact, ",")
|
||||
t.Annotations[podPriorityAllowedAnnotation] = strings.Join(src.Spec.PriorityClasses.Exact, ",")
|
||||
}
|
||||
|
||||
if src.Spec.PriorityClasses.Regex != "" {
|
||||
in.Annotations[podPriorityAllowedRegexAnnotation] = src.Spec.PriorityClasses.Regex
|
||||
t.Annotations[podPriorityAllowedRegexAnnotation] = src.Spec.PriorityClasses.Regex
|
||||
}
|
||||
}
|
||||
|
||||
if src.Spec.ServiceOptions != nil && src.Spec.ServiceOptions.AllowedServices != nil {
|
||||
if src.Spec.ServiceOptions.AllowedServices.NodePort != nil {
|
||||
in.Annotations[enableNodePortsAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.NodePort)
|
||||
t.Annotations[enableNodePortsAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.NodePort)
|
||||
}
|
||||
|
||||
if src.Spec.ServiceOptions.AllowedServices.ExternalName != nil {
|
||||
in.Annotations[enableExternalNameAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.ExternalName)
|
||||
t.Annotations[enableExternalNameAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.ExternalName)
|
||||
}
|
||||
|
||||
if src.Spec.ServiceOptions.AllowedServices.LoadBalancer != nil {
|
||||
in.Annotations[enableLoadBalancerAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.LoadBalancer)
|
||||
t.Annotations[enableLoadBalancerAnnotation] = strconv.FormatBool(*src.Spec.ServiceOptions.AllowedServices.LoadBalancer)
|
||||
}
|
||||
}
|
||||
|
||||
// Status
|
||||
in.Status = TenantStatus{
|
||||
t.Status = TenantStatus{
|
||||
Size: src.Status.Size,
|
||||
Namespaces: src.Status.Namespaces,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -15,30 +15,29 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
capsulev1beta1 "github.com/clastix/capsule/api/v1beta1"
|
||||
)
|
||||
|
||||
//nolint:maintidx
|
||||
// nolint:maintidx
|
||||
func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
||||
var namespaceQuota int32 = 5
|
||||
|
||||
nodeSelector := map[string]string{
|
||||
"foo": "bar",
|
||||
}
|
||||
v1alpha1AdditionalMetadataSpec := &AdditionalMetadata{
|
||||
Labels: map[string]string{
|
||||
v1alpha1AdditionalMetadataSpec := &AdditionalMetadataSpec{
|
||||
AdditionalLabels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
AdditionalAnnotations: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
}
|
||||
v1alpha1AllowedListSpec := &api.AllowedListSpec{
|
||||
v1alpha1AllowedListSpec := &AllowedListSpec{
|
||||
Exact: []string{"foo", "bar"},
|
||||
Regex: "^foo*",
|
||||
}
|
||||
v1beta1AdditionalMetadataSpec := &api.AdditionalMetadataSpec{
|
||||
v1beta1AdditionalMetadataSpec := &capsulev1beta1.AdditionalMetadataSpec{
|
||||
Labels: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
@@ -50,22 +49,20 @@ func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
||||
Quota: &namespaceQuota,
|
||||
AdditionalMetadata: v1beta1AdditionalMetadataSpec,
|
||||
}
|
||||
v1beta1ServiceOptions := &api.ServiceOptions{
|
||||
v1beta1ServiceOptions := &capsulev1beta1.ServiceOptions{
|
||||
AdditionalMetadata: v1beta1AdditionalMetadataSpec,
|
||||
AllowedServices: &api.AllowedServices{
|
||||
NodePort: pointer.Bool(false),
|
||||
ExternalName: pointer.Bool(false),
|
||||
LoadBalancer: pointer.Bool(false),
|
||||
AllowedServices: &capsulev1beta1.AllowedServices{
|
||||
NodePort: pointer.BoolPtr(false),
|
||||
ExternalName: pointer.BoolPtr(false),
|
||||
LoadBalancer: pointer.BoolPtr(false),
|
||||
},
|
||||
ExternalServiceIPs: &api.ExternalServiceIPsSpec{
|
||||
Allowed: []api.AllowedIP{"192.168.0.1"},
|
||||
ExternalServiceIPs: &capsulev1beta1.ExternalServiceIPsSpec{
|
||||
Allowed: []capsulev1beta1.AllowedIP{"192.168.0.1"},
|
||||
},
|
||||
}
|
||||
v1beta2AllowedListSpec := &api.SelectorAllowedListSpec{
|
||||
AllowedListSpec: api.AllowedListSpec{
|
||||
Exact: []string{"foo", "bar"},
|
||||
Regex: "^foo*",
|
||||
},
|
||||
v1beta1AllowedListSpec := &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"foo", "bar"},
|
||||
Regex: "^foo*",
|
||||
}
|
||||
networkPolicies := []networkingv1.NetworkPolicySpec{
|
||||
{
|
||||
@@ -237,25 +234,25 @@ func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
||||
},
|
||||
NamespaceOptions: v1beta1NamespaceOptions,
|
||||
ServiceOptions: v1beta1ServiceOptions,
|
||||
StorageClasses: &v1beta2AllowedListSpec.AllowedListSpec,
|
||||
StorageClasses: v1beta1AllowedListSpec,
|
||||
IngressOptions: capsulev1beta1.IngressOptions{
|
||||
HostnameCollisionScope: api.HostnameCollisionScopeDisabled,
|
||||
AllowedClasses: &v1beta2AllowedListSpec.AllowedListSpec,
|
||||
AllowedHostnames: &v1beta2AllowedListSpec.AllowedListSpec,
|
||||
HostnameCollisionScope: capsulev1beta1.HostnameCollisionScopeDisabled,
|
||||
AllowedClasses: v1beta1AllowedListSpec,
|
||||
AllowedHostnames: v1beta1AllowedListSpec,
|
||||
},
|
||||
ContainerRegistries: &v1beta2AllowedListSpec.AllowedListSpec,
|
||||
ContainerRegistries: v1beta1AllowedListSpec,
|
||||
NodeSelector: nodeSelector,
|
||||
NetworkPolicies: api.NetworkPolicySpec{
|
||||
NetworkPolicies: capsulev1beta1.NetworkPolicySpec{
|
||||
Items: networkPolicies,
|
||||
},
|
||||
LimitRanges: api.LimitRangesSpec{
|
||||
LimitRanges: capsulev1beta1.LimitRangesSpec{
|
||||
Items: limitRanges,
|
||||
},
|
||||
ResourceQuota: api.ResourceQuotaSpec{
|
||||
Scope: api.ResourceQuotaScopeNamespace,
|
||||
ResourceQuota: capsulev1beta1.ResourceQuotaSpec{
|
||||
Scope: capsulev1beta1.ResourceQuotaScopeNamespace,
|
||||
Items: resourceQuotas,
|
||||
},
|
||||
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
|
||||
AdditionalRoleBindings: []capsulev1beta1.AdditionalRoleBindingsSpec{
|
||||
{
|
||||
ClusterRoleName: "crds-rolebinding",
|
||||
Subjects: []rbacv1.Subject{
|
||||
@@ -267,8 +264,8 @@ func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ImagePullPolicies: []api.ImagePullPolicySpec{"Always", "IfNotPresent"},
|
||||
PriorityClasses: &api.AllowedListSpec{
|
||||
ImagePullPolicies: []capsulev1beta1.ImagePullPolicySpec{"Always", "IfNotPresent"},
|
||||
PriorityClasses: &capsulev1beta1.AllowedListSpec{
|
||||
Exact: []string{"default"},
|
||||
Regex: "^tier-.*$",
|
||||
},
|
||||
@@ -326,7 +323,7 @@ func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
||||
NetworkPolicies: networkPolicies,
|
||||
LimitRanges: limitRanges,
|
||||
ResourceQuota: resourceQuotas,
|
||||
AdditionalRoleBindings: []api.AdditionalRoleBindingsSpec{
|
||||
AdditionalRoleBindings: []AdditionalRoleBindingsSpec{
|
||||
{
|
||||
ClusterRoleName: "crds-rolebinding",
|
||||
Subjects: []rbacv1.Subject{
|
||||
@@ -338,8 +335,8 @@ func generateTenantsSpecs() (Tenant, capsulev1beta1.Tenant) {
|
||||
},
|
||||
},
|
||||
},
|
||||
ExternalServiceIPs: &api.ExternalServiceIPsSpec{
|
||||
Allowed: []api.AllowedIP{"192.168.0.1"},
|
||||
ExternalServiceIPs: &ExternalServiceIPsSpec{
|
||||
Allowed: []AllowedIP{"192.168.0.1"},
|
||||
},
|
||||
},
|
||||
Status: TenantStatus{
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
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
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
|
||||
type ExternalServiceIPsSpec struct {
|
||||
Allowed []AllowedIP `json:"allowed"`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package tenant
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
AvailableIngressClassesAnnotation = "capsule.clastix.io/ingress-classes"
|
||||
@@ -11,3 +15,11 @@ const (
|
||||
AllowedRegistriesAnnotation = "capsule.clastix.io/allowed-registries"
|
||||
AllowedRegistriesRegexpAnnotation = "capsule.clastix.io/allowed-registries-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()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -9,16 +9,24 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (in *Tenant) IsFull() bool {
|
||||
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 in.Spec.NamespaceQuota == nil {
|
||||
if t.Spec.NamespaceQuota == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(in.Status.Namespaces) >= int(*in.Spec.NamespaceQuota)
|
||||
return len(t.Status.Namespaces) >= int(*t.Spec.NamespaceQuota)
|
||||
}
|
||||
|
||||
func (in *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
func (t *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
var l []string
|
||||
|
||||
for _, ns := range namespaces {
|
||||
@@ -29,6 +37,6 @@ func (in *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
|
||||
sort.Strings(l)
|
||||
|
||||
in.Status.Namespaces = l
|
||||
in.Status.Size = uint(len(l))
|
||||
t.Status.Namespaces = l
|
||||
t.Status.Size = uint(len(l))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,10 +9,10 @@ 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"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func GetTypeLabel(t metav1.Object) (label string, err error) {
|
||||
func GetTypeLabel(t runtime.Object) (label string, err error) {
|
||||
switch v := t.(type) {
|
||||
case *Tenant:
|
||||
return "capsule.clastix.io/tenant", nil
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
@@ -7,28 +7,26 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// TenantSpec defines the desired state of Tenant.
|
||||
type TenantSpec struct {
|
||||
Owner OwnerSpec `json:"owner"`
|
||||
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
//+kubebuilder:validation:Minimum=1
|
||||
NamespaceQuota *int32 `json:"namespaceQuota,omitempty"`
|
||||
NamespacesMetadata *AdditionalMetadata `json:"namespacesMetadata,omitempty"`
|
||||
ServicesMetadata *AdditionalMetadata `json:"servicesMetadata,omitempty"`
|
||||
StorageClasses *api.AllowedListSpec `json:"storageClasses,omitempty"`
|
||||
IngressClasses *api.AllowedListSpec `json:"ingressClasses,omitempty"`
|
||||
IngressHostnames *api.AllowedListSpec `json:"ingressHostnames,omitempty"`
|
||||
ContainerRegistries *api.AllowedListSpec `json:"containerRegistries,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"`
|
||||
ContainerRegistries *AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
NodeSelector map[string]string `json:"nodeSelector,omitempty"`
|
||||
NetworkPolicies []networkingv1.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
LimitRanges []corev1.LimitRangeSpec `json:"limitRanges,omitempty"`
|
||||
ResourceQuota []corev1.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
AdditionalRoleBindings []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
ExternalServiceIPs *api.ExternalServiceIPsSpec `json:"externalServiceIPs,omitempty"`
|
||||
AdditionalRoleBindings []AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
ExternalServiceIPs *ExternalServiceIPsSpec `json:"externalServiceIPs,omitempty"`
|
||||
}
|
||||
|
||||
// TenantStatus defines the observed state of Tenant.
|
||||
@@ -46,7 +44,6 @@ type TenantStatus struct {
|
||||
// +kubebuilder:printcolumn:name="Owner kind",type="string",JSONPath=".spec.owner.kind",description="The assigned Tenant owner kind"
|
||||
// +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"
|
||||
// +kubebuilder:deprecatedversion:warning="This version is going to be dropped in the upcoming version of Capsule; please, migrate to v1beta2 version."
|
||||
|
||||
// Tenant is the Schema for the tenants API.
|
||||
type Tenant struct {
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"io/ioutil"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
func (in *Tenant) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
||||
certData, _ := os.ReadFile("/tmp/k8s-webhook-server/serving-certs/tls.crt")
|
||||
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(in).
|
||||
For(t).
|
||||
Complete()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
@@ -9,24 +9,24 @@
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/api/networking/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
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.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
if in.AdditionalLabels != nil {
|
||||
in, out := &in.AdditionalLabels, &out.AdditionalLabels
|
||||
*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
|
||||
if in.AdditionalAnnotations != nil {
|
||||
in, out := &in.AdditionalAnnotations, &out.AdditionalAnnotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
@@ -34,12 +34,52 @@ 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 *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
|
||||
}
|
||||
@@ -122,6 +162,26 @@ func (in *CapsuleConfigurationSpec) DeepCopy() *CapsuleConfigurationSpec {
|
||||
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 *OwnerSpec) DeepCopyInto(out *OwnerSpec) {
|
||||
*out = *in
|
||||
@@ -207,32 +267,32 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
}
|
||||
if in.NamespacesMetadata != nil {
|
||||
in, out := &in.NamespacesMetadata, &out.NamespacesMetadata
|
||||
*out = new(AdditionalMetadata)
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ServicesMetadata != nil {
|
||||
in, out := &in.ServicesMetadata, &out.ServicesMetadata
|
||||
*out = new(AdditionalMetadata)
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StorageClasses != nil {
|
||||
in, out := &in.StorageClasses, &out.StorageClasses
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.IngressClasses != nil {
|
||||
in, out := &in.IngressClasses, &out.IngressClasses
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.IngressHostnames != nil {
|
||||
in, out := &in.IngressHostnames, &out.IngressHostnames
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ContainerRegistries != nil {
|
||||
in, out := &in.ContainerRegistries, &out.ContainerRegistries
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NodeSelector != nil {
|
||||
@@ -244,7 +304,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])
|
||||
}
|
||||
@@ -265,14 +325,14 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
}
|
||||
if in.AdditionalRoleBindings != nil {
|
||||
in, out := &in.AdditionalRoleBindings, &out.AdditionalRoleBindings
|
||||
*out = make([]api.AdditionalRoleBindingsSpec, 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(api.ExternalServiceIPsSpec)
|
||||
*out = new(ExternalServiceIPsSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
package v1beta1
|
||||
|
||||
type AdditionalMetadataSpec struct {
|
||||
Labels map[string]string `json:"labels,omitempty"`
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
import rbacv1 "k8s.io/api/rbac/v1"
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
|
||||
type AdditionalRoleBindingsSpec struct {
|
||||
ClusterRoleName string `json:"clusterRoleName"`
|
||||
// kubebuilder:validation:Minimum=1
|
||||
37
api/v1beta1/allowed_list.go
Normal file
37
api/v1beta1/allowed_list.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//nolint:dupl
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
const (
|
||||
DenyWildcard = "capsule.clastix.io/deny-wildcard"
|
||||
denyWildcard = "capsule.clastix.io/deny-wildcard"
|
||||
)
|
||||
|
||||
func (in *Tenant) IsWildcardDenied() bool {
|
||||
if v, ok := in.Annotations[DenyWildcard]; ok && v == "true" {
|
||||
func (t *Tenant) IsWildcardDenied() bool {
|
||||
if v, ok := t.Annotations[denyWildcard]; ok && v == "true" {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
//nolint:dupl
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
@@ -9,14 +9,12 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
|
||||
type ForbiddenListSpec struct {
|
||||
Exact []string `json:"denied,omitempty"`
|
||||
Regex string `json:"deniedRegex,omitempty"`
|
||||
}
|
||||
|
||||
func (in ForbiddenListSpec) ExactMatch(value string) (ok bool) {
|
||||
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])
|
||||
@@ -1,8 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//nolint:dupl
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// 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
|
||||
//+kubebuilder:object:generate=true
|
||||
//+groupName=capsule.clastix.io
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
const (
|
||||
HostnameCollisionScopeCluster HostnameCollisionScope = "Cluster"
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
// +kubebuilder:validation:Enum=Always;Never;IfNotPresent
|
||||
type ImagePullPolicySpec string
|
||||
@@ -1,15 +1,11 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
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 *api.AllowedListSpec `json:"allowedClasses,omitempty"`
|
||||
AllowedClasses *AllowedListSpec `json:"allowedClasses,omitempty"`
|
||||
// Defines the scope of hostname collision check performed when Tenant Owners create Ingress with allowed hostnames.
|
||||
//
|
||||
//
|
||||
@@ -22,7 +18,7 @@ type IngressOptions struct {
|
||||
//
|
||||
// Optional.
|
||||
// +kubebuilder:default=Disabled
|
||||
HostnameCollisionScope api.HostnameCollisionScope `json:"hostnameCollisionScope,omitempty"`
|
||||
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 *api.AllowedListSpec `json:"allowedHostnames,omitempty"`
|
||||
AllowedHostnames *AllowedListSpec `json:"allowedHostnames,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
|
||||
type LimitRangesSpec struct {
|
||||
Items []corev1.LimitRangeSpec `json:"items,omitempty"`
|
||||
}
|
||||
@@ -1,64 +1,57 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
import "strings"
|
||||
|
||||
type NamespaceOptions struct {
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
//+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 *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
AdditionalMetadata *AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
}
|
||||
|
||||
func (in *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsAnnotation]; ok {
|
||||
func (t *Tenant) hasForbiddenNamespaceLabelsAnnotations() bool {
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceLabelsAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceLabelsRegexpAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (in *Tenant) hasForbiddenNamespaceAnnotationsAnnotations() bool {
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; ok {
|
||||
func (t *Tenant) hasForbiddenNamespaceAnnotationsAnnotations() bool {
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceAnnotationsAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
if _, ok := in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
|
||||
if _, ok := t.Annotations[ForbiddenNamespaceAnnotationsRegexpAnnotation]; ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (in *Tenant) ForbiddenUserNamespaceLabels() *api.ForbiddenListSpec {
|
||||
if !in.hasForbiddenNamespaceLabelsAnnotations() {
|
||||
func (t *Tenant) ForbiddenUserNamespaceLabels() *ForbiddenListSpec {
|
||||
if !t.hasForbiddenNamespaceLabelsAnnotations() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &api.ForbiddenListSpec{
|
||||
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceLabelsAnnotation], ","),
|
||||
Regex: in.Annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation],
|
||||
return &ForbiddenListSpec{
|
||||
Exact: strings.Split(t.Annotations[ForbiddenNamespaceLabelsAnnotation], ","),
|
||||
Regex: t.Annotations[ForbiddenNamespaceLabelsRegexpAnnotation],
|
||||
}
|
||||
}
|
||||
|
||||
func (in *Tenant) ForbiddenUserNamespaceAnnotations() *api.ForbiddenListSpec {
|
||||
if !in.hasForbiddenNamespaceAnnotationsAnnotations() {
|
||||
func (t *Tenant) ForbiddenUserNamespaceAnnotations() *ForbiddenListSpec {
|
||||
if !t.hasForbiddenNamespaceAnnotationsAnnotations() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &api.ForbiddenListSpec{
|
||||
Exact: strings.Split(in.Annotations[api.ForbiddenNamespaceAnnotationsAnnotation], ","),
|
||||
Regex: in.Annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation],
|
||||
return &ForbiddenListSpec{
|
||||
Exact: strings.Split(t.Annotations[ForbiddenNamespaceAnnotationsAnnotation], ","),
|
||||
Regex: t.Annotations[ForbiddenNamespaceAnnotationsRegexpAnnotation],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
|
||||
type NetworkPolicySpec struct {
|
||||
Items []networkingv1.NetworkPolicySpec `json:"items,omitempty"`
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
@@ -9,14 +6,14 @@ import (
|
||||
|
||||
type OwnerListSpec []OwnerSpec
|
||||
|
||||
func (in OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec) {
|
||||
sort.Sort(ByKindAndName(in))
|
||||
i := sort.Search(len(in), func(i int) bool {
|
||||
return in[i].Kind >= kind && in[i].Name >= name
|
||||
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(in) && in[i].Kind == kind && in[i].Name == name {
|
||||
return in[i]
|
||||
if i < len(o) && o[i].Kind == kind && o[i].Name == name {
|
||||
return o[i]
|
||||
}
|
||||
|
||||
return
|
||||
@@ -24,18 +21,18 @@ func (in OwnerListSpec) FindOwner(name string, kind OwnerKind) (owner OwnerSpec)
|
||||
|
||||
type ByKindAndName OwnerListSpec
|
||||
|
||||
func (in ByKindAndName) Len() int {
|
||||
return len(in)
|
||||
func (b ByKindAndName) Len() int {
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func (in ByKindAndName) Less(i, j int) bool {
|
||||
if in[i].Kind.String() != in[j].Kind.String() {
|
||||
return in[i].Kind.String() < in[j].Kind.String()
|
||||
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 in[i].Name < in[j].Name
|
||||
return b[i].Name < b[j].Name
|
||||
}
|
||||
|
||||
func (in ByKindAndName) Swap(i, j int) {
|
||||
in[i], in[j] = in[j], in[i]
|
||||
func (b ByKindAndName) Swap(i, j int) {
|
||||
b[i], b[j] = b[j], b[i]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
@@ -19,7 +19,7 @@ const (
|
||||
// 2. the overall length of the annotation key that is exceeding 63 characters
|
||||
// For emails, the symbol @ can be replaced with the placeholder __AT__.
|
||||
// For the latter one, the index of the owner can be used to force the retrieval.
|
||||
func (in *OwnerSpec) GetRoles(tenant Tenant, index int) []string {
|
||||
func (in OwnerSpec) GetRoles(tenant Tenant, index int) []string {
|
||||
for key, value := range tenant.GetAnnotations() {
|
||||
if !strings.HasPrefix(key, fmt.Sprintf("%s/", ClusterRoleNamesAnnotation)) {
|
||||
continue
|
||||
@@ -38,10 +38,36 @@ func (in *OwnerSpec) GetRoles(tenant Tenant, index int) []string {
|
||||
}
|
||||
}
|
||||
|
||||
return []string{"admin", "capsule-namespace-deleter"}
|
||||
roles := []string{"admin", "capsule-namespace-deleter"}
|
||||
|
||||
if tenant.Spec.GitOpsReady {
|
||||
roles = append(roles, in.getGitOpsRoles(tenant)...)
|
||||
}
|
||||
|
||||
return roles
|
||||
}
|
||||
|
||||
func (in *OwnerSpec) convertMap() map[string]string {
|
||||
func (in OwnerSpec) GetClusterRoles(tenant Tenant) []string {
|
||||
if tenant.Spec.GitOpsReady {
|
||||
return in.getGitOpsClusterRoles(tenant)
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (in OwnerSpec) getGitOpsClusterRoles(tenant Tenant) []string {
|
||||
return []string{
|
||||
"capsule-tenant-impersonator-" + tenant.Name + "-" + in.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func (in OwnerSpec) getGitOpsRoles(tenant Tenant) []string {
|
||||
return []string{
|
||||
"cluster-admin",
|
||||
}
|
||||
}
|
||||
|
||||
func (in OwnerSpec) convertMap() map[string]string {
|
||||
return map[string]string{
|
||||
"__AT__": "@",
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package api
|
||||
package v1beta1
|
||||
|
||||
import corev1 "k8s.io/api/core/v1"
|
||||
|
||||
@@ -13,8 +13,6 @@ const (
|
||||
ResourceQuotaScopeNamespace ResourceQuotaScope = "Namespace"
|
||||
)
|
||||
|
||||
// +kubebuilder:object:generate=true
|
||||
|
||||
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
|
||||
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"`
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
type AllowedServices struct {
|
||||
// +kubebuilder:default=true
|
||||
//+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
|
||||
//+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
|
||||
//+kubebuilder:default=true
|
||||
// Specifies if LoadBalancer service type resources are allowed for the Tenant. Default is true. Optional.
|
||||
LoadBalancer *bool `json:"loadBalancer,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
type ServiceOptions struct {
|
||||
// Specifies additional labels and annotations the Capsule operator places on any Service resource in the Tenant. Optional.
|
||||
AdditionalMetadata *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
AdditionalMetadata *AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
// Block or deny certain type of Services. Optional.
|
||||
AllowedServices *api.AllowedServices `json:"allowedServices,omitempty"`
|
||||
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 *api.ExternalServiceIPsSpec `json:"externalIPs,omitempty"`
|
||||
ExternalServiceIPs *ExternalServiceIPsSpec `json:"externalIPs,omitempty"`
|
||||
}
|
||||
|
||||
31
api/v1beta1/tenant_annotations.go
Normal file
31
api/v1beta1/tenant_annotations.go
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
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"
|
||||
ProtectedTenantAnnotation = "capsule.clastix.io/protected"
|
||||
)
|
||||
|
||||
func UsedQuotaFor(resource fmt.Stringer) string {
|
||||
return "quota.capsule.clastix.io/used-" + strings.ReplaceAll(resource.String(), "/", "_")
|
||||
}
|
||||
|
||||
func HardQuotaFor(resource fmt.Stringer) string {
|
||||
return "quota.capsule.clastix.io/hard-" + strings.ReplaceAll(resource.String(), "/", "_")
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
@@ -9,24 +9,24 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (in *Tenant) IsCordoned() bool {
|
||||
if v, ok := in.Labels["capsule.clastix.io/cordon"]; ok && v == "enabled" {
|
||||
func (t *Tenant) IsCordoned() bool {
|
||||
if v, ok := t.Labels["capsule.clastix.io/cordon"]; ok && v == "enabled" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (in *Tenant) IsFull() bool {
|
||||
func (t *Tenant) IsFull() bool {
|
||||
// we don't have limits on assigned Namespaces
|
||||
if in.Spec.NamespaceOptions == nil || in.Spec.NamespaceOptions.Quota == nil {
|
||||
if t.Spec.NamespaceOptions == nil || t.Spec.NamespaceOptions.Quota == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(in.Status.Namespaces) >= int(*in.Spec.NamespaceOptions.Quota)
|
||||
return len(t.Status.Namespaces) >= int(*t.Spec.NamespaceOptions.Quota)
|
||||
}
|
||||
|
||||
func (in *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
func (t *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
var l []string
|
||||
|
||||
for _, ns := range namespaces {
|
||||
@@ -37,10 +37,10 @@ func (in *Tenant) AssignNamespaces(namespaces []corev1.Namespace) {
|
||||
|
||||
sort.Strings(l)
|
||||
|
||||
in.Status.Namespaces = l
|
||||
in.Status.Size = uint(len(l))
|
||||
t.Status.Namespaces = l
|
||||
t.Status.Size = uint(len(l))
|
||||
}
|
||||
|
||||
func (in *Tenant) GetOwnerProxySettings(name string, kind OwnerKind) []ProxySettings {
|
||||
return in.Spec.Owners.FindOwner(name, kind).ProxyOperations
|
||||
func (t *Tenant) GetOwnerProxySettings(name string, kind OwnerKind) []ProxySettings {
|
||||
return t.Spec.Owners.FindOwner(name, kind).ProxyOperations
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package utils
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -10,15 +10,11 @@ import (
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/projectcapsule/capsule/api/v1alpha1"
|
||||
"github.com/projectcapsule/capsule/api/v1beta1"
|
||||
"github.com/projectcapsule/capsule/api/v1beta2"
|
||||
)
|
||||
|
||||
func GetTypeLabel(t runtime.Object) (label string, err error) {
|
||||
switch v := t.(type) {
|
||||
case *v1alpha1.Tenant, *v1beta1.Tenant, *v1beta2.Tenant:
|
||||
case *Tenant:
|
||||
return "capsule.clastix.io/tenant", nil
|
||||
case *corev1.LimitRange:
|
||||
return "capsule.clastix.io/limit-range", nil
|
||||
@@ -28,6 +24,8 @@ func GetTypeLabel(t runtime.Object) (label string, err error) {
|
||||
return "capsule.clastix.io/resource-quota", nil
|
||||
case *rbacv1.RoleBinding:
|
||||
return "capsule.clastix.io/role-binding", nil
|
||||
case *rbacv1.ClusterRoleBinding:
|
||||
return "capsule.clastix.io/cluster-role-binding", nil
|
||||
default:
|
||||
err = fmt.Errorf("type %T is not mapped as Capsule label recognized", v)
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
@@ -13,7 +13,7 @@ const (
|
||||
|
||||
// Returns the observed state of the Tenant.
|
||||
type TenantStatus struct {
|
||||
// +kubebuilder:default=Active
|
||||
//+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.
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// TenantSpec defines the desired state of Tenant.
|
||||
@@ -16,31 +14,34 @@ type TenantSpec struct {
|
||||
// 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 *api.ServiceOptions `json:"serviceOptions,omitempty"`
|
||||
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 *api.AllowedListSpec `json:"storageClasses,omitempty"`
|
||||
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 *api.AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
ContainerRegistries *AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// Specifies the label to control the placement of pods on a given pool of worker nodes. All namespaces 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 api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
NetworkPolicies NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
LimitRanges api.LimitRangesSpec `json:"limitRanges,omitempty"`
|
||||
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 api.ResourceQuotaSpec `json:"resourceQuotas,omitempty"`
|
||||
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 []api.AdditionalRoleBindingsSpec `json:"additionalRoleBindings,omitempty"`
|
||||
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 []api.ImagePullPolicySpec `json:"imagePullPolicies,omitempty"`
|
||||
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 *api.AllowedListSpec `json:"priorityClasses,omitempty"`
|
||||
PriorityClasses *AllowedListSpec `json:"priorityClasses,omitempty"`
|
||||
// Configured RBAC for machine owners tailored for GitOps controllers.
|
||||
GitOpsReady bool `json:"gitOpsReady,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
//+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"
|
||||
@@ -57,9 +58,9 @@ type Tenant struct {
|
||||
Status TenantStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (in *Tenant) Hub() {}
|
||||
func (t *Tenant) Hub() {}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// TenantList contains a list of Tenant.
|
||||
type TenantList struct {
|
||||
@@ -71,11 +72,3 @@ type TenantList struct {
|
||||
func init() {
|
||||
SchemeBuilder.Register(&Tenant{}, &TenantList{})
|
||||
}
|
||||
|
||||
func (in *Tenant) GetNamespaces() (res []string) {
|
||||
res = make([]string, 0, len(in.Status.Namespaces))
|
||||
|
||||
res = append(res, in.Status.Namespaces...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
)
|
||||
|
||||
func (in *Tenant) SetupWebhookWithManager(mgr ctrl.Manager) error {
|
||||
certData, _ := os.ReadFile("/tmp/k8s-webhook-server/serving-certs/tls.crt")
|
||||
if len(certData) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ctrl.NewWebhookManagedBy(mgr).
|
||||
For(in).
|
||||
Complete()
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// Copyright 2020-2021 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
@@ -9,10 +9,81 @@
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
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
|
||||
@@ -64,17 +135,57 @@ func (in ByKindAndName) DeepCopy() ByKindAndName {
|
||||
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(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.AllowedHostnames != nil {
|
||||
in, out := &in.AllowedHostnames, &out.AllowedHostnames
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
@@ -89,6 +200,28 @@ func (in *IngressOptions) DeepCopy() *IngressOptions {
|
||||
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
|
||||
@@ -99,7 +232,7 @@ func (in *NamespaceOptions) DeepCopyInto(out *NamespaceOptions) {
|
||||
}
|
||||
if in.AdditionalMetadata != nil {
|
||||
in, out := &in.AdditionalMetadata, &out.AdditionalMetadata
|
||||
*out = new(api.AdditionalMetadataSpec)
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
@@ -114,6 +247,28 @@ func (in *NamespaceOptions) DeepCopy() *NamespaceOptions {
|
||||
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 *NonLimitedResourceError) DeepCopyInto(out *NonLimitedResourceError) {
|
||||
*out = *in
|
||||
@@ -192,22 +347,44 @@ func (in *ProxySettings) DeepCopy() *ProxySettings {
|
||||
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(api.AdditionalMetadataSpec)
|
||||
*out = new(AdditionalMetadataSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.AllowedServices != nil {
|
||||
in, out := &in.AllowedServices, &out.AllowedServices
|
||||
*out = new(api.AllowedServices)
|
||||
*out = new(AllowedServices)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ExternalServiceIPs != nil {
|
||||
in, out := &in.ExternalServiceIPs, &out.ExternalServiceIPs
|
||||
*out = new(api.ExternalServiceIPsSpec)
|
||||
*out = new(ExternalServiceIPsSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
@@ -298,18 +475,18 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
}
|
||||
if in.ServiceOptions != nil {
|
||||
in, out := &in.ServiceOptions, &out.ServiceOptions
|
||||
*out = new(api.ServiceOptions)
|
||||
*out = new(ServiceOptions)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.StorageClasses != nil {
|
||||
in, out := &in.StorageClasses, &out.StorageClasses
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.IngressOptions.DeepCopyInto(&out.IngressOptions)
|
||||
if in.ContainerRegistries != nil {
|
||||
in, out := &in.ContainerRegistries, &out.ContainerRegistries
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.NodeSelector != nil {
|
||||
@@ -324,19 +501,19 @@ func (in *TenantSpec) DeepCopyInto(out *TenantSpec) {
|
||||
in.ResourceQuota.DeepCopyInto(&out.ResourceQuota)
|
||||
if in.AdditionalRoleBindings != nil {
|
||||
in, out := &in.AdditionalRoleBindings, &out.AdditionalRoleBindings
|
||||
*out = make([]api.AdditionalRoleBindingsSpec, len(*in))
|
||||
*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([]api.ImagePullPolicySpec, len(*in))
|
||||
*out = make([]ImagePullPolicySpec, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.PriorityClasses != nil {
|
||||
in, out := &in.PriorityClasses, &out.PriorityClasses
|
||||
*out = new(api.AllowedListSpec)
|
||||
*out = new(AllowedListSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/conversion"
|
||||
|
||||
capsulev1alpha1 "github.com/projectcapsule/capsule/api/v1alpha1"
|
||||
)
|
||||
|
||||
func (in *CapsuleConfiguration) ConvertTo(raw conversion.Hub) error {
|
||||
dst, ok := raw.(*capsulev1alpha1.CapsuleConfiguration)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected type *capsulev1alpha1.CapsuleConfiguration, got %T", dst)
|
||||
}
|
||||
|
||||
dst.ObjectMeta = in.ObjectMeta
|
||||
dst.Spec.ProtectedNamespaceRegexpString = in.Spec.ProtectedNamespaceRegexpString
|
||||
dst.Spec.UserGroups = in.Spec.UserGroups
|
||||
dst.Spec.ProtectedNamespaceRegexpString = in.Spec.ProtectedNamespaceRegexpString
|
||||
|
||||
annotations := dst.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
if in.Spec.NodeMetadata != nil {
|
||||
if len(in.Spec.NodeMetadata.ForbiddenLabels.Exact) > 0 {
|
||||
annotations[capsulev1alpha1.ForbiddenNodeLabelsAnnotation] = strings.Join(in.Spec.NodeMetadata.ForbiddenLabels.Exact, ",")
|
||||
}
|
||||
|
||||
if len(in.Spec.NodeMetadata.ForbiddenLabels.Regex) > 0 {
|
||||
annotations[capsulev1alpha1.ForbiddenNodeLabelsRegexpAnnotation] = in.Spec.NodeMetadata.ForbiddenLabels.Regex
|
||||
}
|
||||
|
||||
if len(in.Spec.NodeMetadata.ForbiddenAnnotations.Exact) > 0 {
|
||||
annotations[capsulev1alpha1.ForbiddenNodeAnnotationsAnnotation] = strings.Join(in.Spec.NodeMetadata.ForbiddenAnnotations.Exact, ",")
|
||||
}
|
||||
|
||||
if len(in.Spec.NodeMetadata.ForbiddenAnnotations.Regex) > 0 {
|
||||
annotations[capsulev1alpha1.ForbiddenNodeAnnotationsRegexpAnnotation] = in.Spec.NodeMetadata.ForbiddenAnnotations.Regex
|
||||
}
|
||||
}
|
||||
|
||||
annotations[capsulev1alpha1.EnableTLSConfigurationAnnotationName] = fmt.Sprintf("%t", in.Spec.EnableTLSReconciler)
|
||||
annotations[capsulev1alpha1.TLSSecretNameAnnotation] = in.Spec.CapsuleResources.TLSSecretName
|
||||
annotations[capsulev1alpha1.MutatingWebhookConfigurationName] = in.Spec.CapsuleResources.MutatingWebhookConfigurationName
|
||||
annotations[capsulev1alpha1.ValidatingWebhookConfigurationName] = in.Spec.CapsuleResources.ValidatingWebhookConfigurationName
|
||||
|
||||
dst.SetAnnotations(annotations)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (in *CapsuleConfiguration) ConvertFrom(raw conversion.Hub) error {
|
||||
src, ok := raw.(*capsulev1alpha1.CapsuleConfiguration)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected type *capsulev1alpha1.CapsuleConfiguration, got %T", src)
|
||||
}
|
||||
|
||||
in.ObjectMeta = src.ObjectMeta
|
||||
in.Spec.ProtectedNamespaceRegexpString = src.Spec.ProtectedNamespaceRegexpString
|
||||
in.Spec.UserGroups = src.Spec.UserGroups
|
||||
in.Spec.ProtectedNamespaceRegexpString = src.Spec.ProtectedNamespaceRegexpString
|
||||
|
||||
annotations := src.GetAnnotations()
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.ForbiddenNodeLabelsAnnotation]; found {
|
||||
if in.Spec.NodeMetadata == nil {
|
||||
in.Spec.NodeMetadata = &NodeMetadata{}
|
||||
}
|
||||
|
||||
in.Spec.NodeMetadata.ForbiddenLabels.Exact = strings.Split(value, ",")
|
||||
|
||||
delete(annotations, capsulev1alpha1.ForbiddenNodeLabelsAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.ForbiddenNodeLabelsRegexpAnnotation]; found {
|
||||
if in.Spec.NodeMetadata == nil {
|
||||
in.Spec.NodeMetadata = &NodeMetadata{}
|
||||
}
|
||||
|
||||
in.Spec.NodeMetadata.ForbiddenLabels.Regex = value
|
||||
|
||||
delete(annotations, capsulev1alpha1.ForbiddenNodeLabelsRegexpAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.ForbiddenNodeAnnotationsAnnotation]; found {
|
||||
if in.Spec.NodeMetadata == nil {
|
||||
in.Spec.NodeMetadata = &NodeMetadata{}
|
||||
}
|
||||
|
||||
in.Spec.NodeMetadata.ForbiddenAnnotations.Exact = strings.Split(value, ",")
|
||||
|
||||
delete(annotations, capsulev1alpha1.ForbiddenNodeAnnotationsAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.ForbiddenNodeAnnotationsRegexpAnnotation]; found {
|
||||
if in.Spec.NodeMetadata == nil {
|
||||
in.Spec.NodeMetadata = &NodeMetadata{}
|
||||
}
|
||||
|
||||
in.Spec.NodeMetadata.ForbiddenAnnotations.Regex = value
|
||||
|
||||
delete(annotations, capsulev1alpha1.ForbiddenNodeAnnotationsRegexpAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.EnableTLSConfigurationAnnotationName]; found {
|
||||
v, _ := strconv.ParseBool(value)
|
||||
|
||||
in.Spec.EnableTLSReconciler = v
|
||||
|
||||
delete(annotations, capsulev1alpha1.EnableTLSConfigurationAnnotationName)
|
||||
}
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.TLSSecretNameAnnotation]; found {
|
||||
in.Spec.CapsuleResources.TLSSecretName = value
|
||||
|
||||
delete(annotations, capsulev1alpha1.TLSSecretNameAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.MutatingWebhookConfigurationName]; found {
|
||||
in.Spec.CapsuleResources.MutatingWebhookConfigurationName = value
|
||||
|
||||
delete(annotations, capsulev1alpha1.MutatingWebhookConfigurationName)
|
||||
}
|
||||
|
||||
if value, found := annotations[capsulev1alpha1.ValidatingWebhookConfigurationName]; found {
|
||||
in.Spec.CapsuleResources.ValidatingWebhookConfigurationName = value
|
||||
|
||||
delete(annotations, capsulev1alpha1.ValidatingWebhookConfigurationName)
|
||||
}
|
||||
|
||||
in.SetAnnotations(annotations)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
// Allows to set different name rather than the canonical one for the Capsule configuration objects,
|
||||
// such as webhook secret or configurations.
|
||||
// +kubebuilder:default={TLSSecretName:"capsule-tls",mutatingWebhookConfigurationName:"capsule-mutating-webhook-configuration",validatingWebhookConfigurationName:"capsule-validating-webhook-configuration"}
|
||||
CapsuleResources CapsuleResources `json:"overrides,omitempty"`
|
||||
// Allows to set the forbidden metadata for the worker nodes that could be patched by a Tenant.
|
||||
// This applies only if the Tenant has an active NodeSelector, and the Owner have right to patch their nodes.
|
||||
NodeMetadata *NodeMetadata `json:"nodeMetadata,omitempty"`
|
||||
// Toggles the TLS reconciler, the controller that is able to generate CA and certificates for the webhooks
|
||||
// when not using an already provided CA and certificate, or when these are managed externally with Vault, or cert-manager.
|
||||
// +kubebuilder:default=true
|
||||
EnableTLSReconciler bool `json:"enableTLSReconciler"` //nolint:tagliatelle
|
||||
}
|
||||
|
||||
type NodeMetadata struct {
|
||||
// Define the labels that a Tenant Owner cannot set for their nodes.
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their nodes.
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations"`
|
||||
}
|
||||
|
||||
type CapsuleResources struct {
|
||||
// Defines the Secret name used for the webhook server.
|
||||
// Must be in the same Namespace where the Capsule Deployment is deployed.
|
||||
// +kubebuilder:default=capsule-tls
|
||||
TLSSecretName string `json:"TLSSecretName"` //nolint:tagliatelle
|
||||
// Name of the MutatingWebhookConfiguration which contains the dynamic admission controller paths and resources.
|
||||
// +kubebuilder:default=capsule-mutating-webhook-configuration
|
||||
MutatingWebhookConfigurationName string `json:"mutatingWebhookConfigurationName"`
|
||||
// Name of the ValidatingWebhookConfiguration which contains the dynamic admission controller paths and resources.
|
||||
// +kubebuilder:default=capsule-validating-webhook-configuration
|
||||
ValidatingWebhookConfigurationName string `json:"validatingWebhookConfigurationName"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
// +kubebuilder:storageversion
|
||||
|
||||
// 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{})
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const (
|
||||
ResourceQuotaAnnotationPrefix = "quota.resources.capsule.clastix.io"
|
||||
ResourceUsedAnnotationPrefix = "used.resources.capsule.clastix.io"
|
||||
)
|
||||
|
||||
func UsedAnnotationForResource(kindGroup string) string {
|
||||
return fmt.Sprintf("%s/%s", ResourceUsedAnnotationPrefix, kindGroup)
|
||||
}
|
||||
|
||||
func LimitAnnotationForResource(kindGroup string) string {
|
||||
return fmt.Sprintf("%s/%s", ResourceQuotaAnnotationPrefix, kindGroup)
|
||||
}
|
||||
|
||||
func GetUsedResourceFromTenant(tenant Tenant, kindGroup string) (int64, error) {
|
||||
usedStr, ok := tenant.GetAnnotations()[UsedAnnotationForResource(kindGroup)]
|
||||
if !ok {
|
||||
usedStr = "0"
|
||||
}
|
||||
|
||||
used, _ := strconv.ParseInt(usedStr, 10, 10)
|
||||
|
||||
return used, nil
|
||||
}
|
||||
|
||||
type NonLimitedResourceError struct {
|
||||
kindGroup string
|
||||
}
|
||||
|
||||
func NewNonLimitedResourceError(kindGroup string) *NonLimitedResourceError {
|
||||
return &NonLimitedResourceError{kindGroup: kindGroup}
|
||||
}
|
||||
|
||||
func (n NonLimitedResourceError) Error() string {
|
||||
return fmt.Sprintf("resource %s is not limited for the current tenant", n.kindGroup)
|
||||
}
|
||||
|
||||
func GetLimitResourceFromTenant(tenant Tenant, kindGroup string) (int64, error) {
|
||||
limitStr, ok := tenant.GetAnnotations()[LimitAnnotationForResource(kindGroup)]
|
||||
if !ok {
|
||||
return 0, NewNonLimitedResourceError(kindGroup)
|
||||
}
|
||||
|
||||
limit, err := strconv.ParseInt(limitStr, 10, 10)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("resource %s limit cannot be parsed, %w", kindGroup, err)
|
||||
}
|
||||
|
||||
return limit, nil
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package v1beta2 contains API Schema definitions for the capsule v1beta2 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=capsule.clastix.io
|
||||
package v1beta2
|
||||
|
||||
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: "v1beta2"}
|
||||
|
||||
// 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
|
||||
)
|
||||
@@ -1,33 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
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.
|
||||
// A default value can be specified, and all the Ingress resources created will inherit the declared class.
|
||||
// Optional.
|
||||
AllowedClasses *api.DefaultAllowedListSpec `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 api.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 *api.AllowedListSpec `json:"allowedHostnames,omitempty"`
|
||||
// Toggles the ability for Ingress resources created in a Tenant to have a hostname wildcard.
|
||||
AllowWildcardHostnames bool `json:"allowWildcardHostnames,omitempty"`
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
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 *api.AdditionalMetadataSpec `json:"additionalMetadata,omitempty"`
|
||||
// Define the labels that a Tenant Owner cannot set for their Namespace resources.
|
||||
ForbiddenLabels api.ForbiddenListSpec `json:"forbiddenLabels,omitempty"`
|
||||
// Define the annotations that a Tenant Owner cannot set for their Namespace resources.
|
||||
ForbiddenAnnotations api.ForbiddenListSpec `json:"forbiddenAnnotations,omitempty"`
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
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"`
|
||||
// Defines additional cluster-roles for the specific Owner.
|
||||
// +kubebuilder:default={admin,capsule-namespace-deleter}
|
||||
ClusterRoles []string `json:"clusterRoles,omitempty"`
|
||||
// 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;RuntimeClasses;PersistentVolumes
|
||||
type ProxyServiceKind string
|
||||
|
||||
func (p ProxyServiceKind) String() string {
|
||||
return string(p)
|
||||
}
|
||||
|
||||
const (
|
||||
NodesProxy ProxyServiceKind = "Nodes"
|
||||
StorageClassesProxy ProxyServiceKind = "StorageClasses"
|
||||
IngressClassesProxy ProxyServiceKind = "IngressClasses"
|
||||
PriorityClassesProxy ProxyServiceKind = "PriorityClasses"
|
||||
RuntimeClassesProxy ProxyServiceKind = "RuntimeClasses"
|
||||
PersistentVolumesProxy ProxyServiceKind = "PersistentVolumes"
|
||||
|
||||
ListOperation ProxyOperation = "List"
|
||||
UpdateOperation ProxyOperation = "Update"
|
||||
DeleteOperation ProxyOperation = "Delete"
|
||||
|
||||
UserOwner OwnerKind = "User"
|
||||
GroupOwner OwnerKind = "Group"
|
||||
ServiceAccountOwner OwnerKind = "ServiceAccount"
|
||||
)
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
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]
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestOwnerListSpec_FindOwner(t *testing.T) {
|
||||
bla := OwnerSpec{
|
||||
Kind: UserOwner,
|
||||
Name: "bla",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: IngressClassesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
bar := OwnerSpec{
|
||||
Kind: GroupOwner,
|
||||
Name: "bar",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
baz := OwnerSpec{
|
||||
Kind: UserOwner,
|
||||
Name: "baz",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"Update"},
|
||||
},
|
||||
},
|
||||
}
|
||||
fim := OwnerSpec{
|
||||
Kind: ServiceAccountOwner,
|
||||
Name: "fim",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: NodesProxy,
|
||||
Operations: []ProxyOperation{"List"},
|
||||
},
|
||||
},
|
||||
}
|
||||
bom := OwnerSpec{
|
||||
Kind: GroupOwner,
|
||||
Name: "bom",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
{
|
||||
Kind: NodesProxy,
|
||||
Operations: []ProxyOperation{"Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
qip := OwnerSpec{
|
||||
Kind: ServiceAccountOwner,
|
||||
Name: "qip",
|
||||
ProxyOperations: []ProxySettings{
|
||||
{
|
||||
Kind: StorageClassesProxy,
|
||||
Operations: []ProxyOperation{"List", "Delete"},
|
||||
},
|
||||
},
|
||||
}
|
||||
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{})
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"crypto/md5" //#nosec
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
// Annotation name part must be no more than 63 characters.
|
||||
maxAnnotationLength = 63
|
||||
)
|
||||
|
||||
func createAnnotation(format string, resource fmt.Stringer) (string, error) {
|
||||
suffix := resource.String()
|
||||
|
||||
hash := md5.Sum([]byte(resource.String())) //#nosec
|
||||
|
||||
hashed := hex.EncodeToString(hash[:])
|
||||
capsuleHashed := format + hashed
|
||||
capsuleAnnotation := format + suffix
|
||||
|
||||
switch {
|
||||
case len(capsuleAnnotation) <= maxAnnotationLength:
|
||||
return capsuleAnnotation, nil
|
||||
case len(capsuleHashed) <= maxAnnotationLength:
|
||||
return capsuleHashed, nil
|
||||
case len(hashed) <= maxAnnotationLength:
|
||||
return hashed, nil
|
||||
default:
|
||||
return "", fmt.Errorf("the annotation name would exceed the maximum supported length (%d), skipping", maxAnnotationLength)
|
||||
}
|
||||
}
|
||||
|
||||
func UsedQuotaFor(resource fmt.Stringer) (string, error) {
|
||||
return createAnnotation("quota.capsule.clastix.io/used-", resource)
|
||||
}
|
||||
|
||||
func HardQuotaFor(resource fmt.Stringer) (string, error) {
|
||||
return createAnnotation("quota.capsule.clastix.io/hard-", resource)
|
||||
}
|
||||
@@ -1,289 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/conversion"
|
||||
|
||||
capsulev1beta1 "github.com/projectcapsule/capsule/api/v1beta1"
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
func (in *Tenant) ConvertFrom(raw conversion.Hub) error {
|
||||
src, ok := raw.(*capsulev1beta1.Tenant)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected *capsulev1beta1.Tenant, got %T", raw)
|
||||
}
|
||||
|
||||
annotations := src.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
|
||||
in.ObjectMeta = src.ObjectMeta
|
||||
in.Spec.Owners = make(OwnerListSpec, 0, len(src.Spec.Owners))
|
||||
|
||||
for index, owner := range src.Spec.Owners {
|
||||
proxySettings := make([]ProxySettings, 0, len(owner.ProxyOperations))
|
||||
|
||||
for _, proxyOp := range owner.ProxyOperations {
|
||||
ops := make([]ProxyOperation, 0, len(proxyOp.Operations))
|
||||
|
||||
for _, op := range proxyOp.Operations {
|
||||
ops = append(ops, ProxyOperation(op))
|
||||
}
|
||||
|
||||
proxySettings = append(proxySettings, ProxySettings{
|
||||
Kind: ProxyServiceKind(proxyOp.Kind),
|
||||
Operations: ops,
|
||||
})
|
||||
}
|
||||
|
||||
in.Spec.Owners = append(in.Spec.Owners, OwnerSpec{
|
||||
Kind: OwnerKind(owner.Kind),
|
||||
Name: owner.Name,
|
||||
ClusterRoles: owner.GetRoles(*src, index),
|
||||
ProxyOperations: proxySettings,
|
||||
})
|
||||
}
|
||||
|
||||
if nsOpts := src.Spec.NamespaceOptions; nsOpts != nil {
|
||||
in.Spec.NamespaceOptions = &NamespaceOptions{}
|
||||
|
||||
in.Spec.NamespaceOptions.Quota = src.Spec.NamespaceOptions.Quota
|
||||
|
||||
in.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceLabelsAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenLabels.Exact = strings.Split(value, ",")
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceLabelsAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenLabels.Regex = value
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceLabelsRegexpAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceAnnotationsAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenAnnotations.Exact = strings.Split(value, ",")
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceAnnotationsAnnotation)
|
||||
}
|
||||
|
||||
if value, found := annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation]; found {
|
||||
in.Spec.NamespaceOptions.ForbiddenAnnotations.Regex = value
|
||||
|
||||
delete(annotations, api.ForbiddenNamespaceAnnotationsRegexpAnnotation)
|
||||
}
|
||||
}
|
||||
|
||||
in.Spec.ServiceOptions = src.Spec.ServiceOptions
|
||||
if src.Spec.StorageClasses != nil {
|
||||
in.Spec.StorageClasses = &api.DefaultAllowedListSpec{
|
||||
SelectorAllowedListSpec: api.SelectorAllowedListSpec{
|
||||
AllowedListSpec: *src.Spec.StorageClasses,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if scope := src.Spec.IngressOptions.HostnameCollisionScope; len(scope) > 0 {
|
||||
in.Spec.IngressOptions.HostnameCollisionScope = scope
|
||||
}
|
||||
|
||||
v, found := annotations[capsulev1beta1.DenyWildcard]
|
||||
if found {
|
||||
value, err := strconv.ParseBool(v)
|
||||
if err == nil {
|
||||
in.Spec.IngressOptions.AllowWildcardHostnames = !value
|
||||
|
||||
delete(annotations, capsulev1beta1.DenyWildcard)
|
||||
}
|
||||
}
|
||||
|
||||
if ingressClass := src.Spec.IngressOptions.AllowedClasses; ingressClass != nil {
|
||||
in.Spec.IngressOptions.AllowedClasses = &api.DefaultAllowedListSpec{
|
||||
SelectorAllowedListSpec: api.SelectorAllowedListSpec{
|
||||
AllowedListSpec: *ingressClass,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if hostnames := src.Spec.IngressOptions.AllowedHostnames; hostnames != nil {
|
||||
in.Spec.IngressOptions.AllowedHostnames = hostnames
|
||||
}
|
||||
|
||||
in.Spec.ContainerRegistries = src.Spec.ContainerRegistries
|
||||
in.Spec.NodeSelector = src.Spec.NodeSelector
|
||||
in.Spec.NetworkPolicies = src.Spec.NetworkPolicies
|
||||
in.Spec.LimitRanges = src.Spec.LimitRanges
|
||||
in.Spec.ResourceQuota = src.Spec.ResourceQuota
|
||||
in.Spec.AdditionalRoleBindings = src.Spec.AdditionalRoleBindings
|
||||
in.Spec.ImagePullPolicies = src.Spec.ImagePullPolicies
|
||||
|
||||
if src.Spec.PriorityClasses != nil {
|
||||
in.Spec.PriorityClasses = &api.DefaultAllowedListSpec{
|
||||
SelectorAllowedListSpec: api.SelectorAllowedListSpec{
|
||||
AllowedListSpec: *src.Spec.PriorityClasses,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if v, found := annotations["capsule.clastix.io/cordon"]; found {
|
||||
value, err := strconv.ParseBool(v)
|
||||
if err == nil {
|
||||
delete(annotations, "capsule.clastix.io/cordon")
|
||||
}
|
||||
|
||||
in.Spec.Cordoned = value
|
||||
}
|
||||
|
||||
if _, found := annotations[api.ProtectedTenantAnnotation]; found {
|
||||
in.Spec.PreventDeletion = true
|
||||
|
||||
delete(annotations, api.ProtectedTenantAnnotation)
|
||||
}
|
||||
|
||||
in.SetAnnotations(annotations)
|
||||
|
||||
in.Status.Namespaces = src.Status.Namespaces
|
||||
in.Status.Size = src.Status.Size
|
||||
|
||||
switch src.Status.State {
|
||||
case capsulev1beta1.TenantStateActive:
|
||||
in.Status.State = TenantStateActive
|
||||
case capsulev1beta1.TenantStateCordoned:
|
||||
in.Status.State = TenantStateCordoned
|
||||
default:
|
||||
in.Status.State = TenantStateActive
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (in *Tenant) ConvertTo(raw conversion.Hub) error {
|
||||
dst, ok := raw.(*capsulev1beta1.Tenant)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected *capsulev1beta1.Tenant, got %T", raw)
|
||||
}
|
||||
|
||||
annotations := in.GetAnnotations()
|
||||
if annotations == nil {
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
|
||||
dst.ObjectMeta = in.ObjectMeta
|
||||
dst.Spec.Owners = make(capsulev1beta1.OwnerListSpec, 0, len(in.Spec.Owners))
|
||||
|
||||
for index, owner := range in.Spec.Owners {
|
||||
proxySettings := make([]capsulev1beta1.ProxySettings, 0, len(owner.ProxyOperations))
|
||||
|
||||
for _, proxyOp := range owner.ProxyOperations {
|
||||
ops := make([]capsulev1beta1.ProxyOperation, 0, len(proxyOp.Operations))
|
||||
|
||||
for _, op := range proxyOp.Operations {
|
||||
ops = append(ops, capsulev1beta1.ProxyOperation(op))
|
||||
}
|
||||
|
||||
proxySettings = append(proxySettings, capsulev1beta1.ProxySettings{
|
||||
Kind: capsulev1beta1.ProxyServiceKind(proxyOp.Kind),
|
||||
Operations: ops,
|
||||
})
|
||||
}
|
||||
|
||||
dst.Spec.Owners = append(dst.Spec.Owners, capsulev1beta1.OwnerSpec{
|
||||
Kind: capsulev1beta1.OwnerKind(owner.Kind),
|
||||
Name: owner.Name,
|
||||
ProxyOperations: proxySettings,
|
||||
})
|
||||
|
||||
if clusterRoles := owner.ClusterRoles; len(clusterRoles) > 0 {
|
||||
annotations[fmt.Sprintf("%s/%d", capsulev1beta1.ClusterRoleNamesAnnotation, index)] = strings.Join(owner.ClusterRoles, ",")
|
||||
}
|
||||
}
|
||||
|
||||
if nsOpts := in.Spec.NamespaceOptions; nsOpts != nil {
|
||||
dst.Spec.NamespaceOptions = &capsulev1beta1.NamespaceOptions{}
|
||||
|
||||
dst.Spec.NamespaceOptions.Quota = nsOpts.Quota
|
||||
dst.Spec.NamespaceOptions.AdditionalMetadata = nsOpts.AdditionalMetadata
|
||||
|
||||
if exact := nsOpts.ForbiddenAnnotations.Exact; len(exact) > 0 {
|
||||
annotations[api.ForbiddenNamespaceAnnotationsAnnotation] = strings.Join(exact, ",")
|
||||
}
|
||||
|
||||
if regex := nsOpts.ForbiddenAnnotations.Regex; len(regex) > 0 {
|
||||
annotations[api.ForbiddenNamespaceAnnotationsRegexpAnnotation] = regex
|
||||
}
|
||||
|
||||
if exact := nsOpts.ForbiddenLabels.Exact; len(exact) > 0 {
|
||||
annotations[api.ForbiddenNamespaceLabelsAnnotation] = strings.Join(exact, ",")
|
||||
}
|
||||
|
||||
if regex := nsOpts.ForbiddenLabels.Regex; len(regex) > 0 {
|
||||
annotations[api.ForbiddenNamespaceLabelsRegexpAnnotation] = regex
|
||||
}
|
||||
}
|
||||
|
||||
dst.Spec.ServiceOptions = in.Spec.ServiceOptions
|
||||
if in.Spec.StorageClasses != nil {
|
||||
dst.Spec.StorageClasses = &in.Spec.StorageClasses.AllowedListSpec
|
||||
}
|
||||
|
||||
dst.Spec.IngressOptions.HostnameCollisionScope = in.Spec.IngressOptions.HostnameCollisionScope
|
||||
|
||||
if allowed := in.Spec.IngressOptions.AllowedClasses; allowed != nil {
|
||||
dst.Spec.IngressOptions.AllowedClasses = &allowed.AllowedListSpec
|
||||
}
|
||||
|
||||
if allowed := in.Spec.IngressOptions.AllowedHostnames; allowed != nil {
|
||||
dst.Spec.IngressOptions.AllowedHostnames = allowed
|
||||
}
|
||||
|
||||
annotations[capsulev1beta1.DenyWildcard] = fmt.Sprintf("%t", !in.Spec.IngressOptions.AllowWildcardHostnames)
|
||||
|
||||
if allowed := in.Spec.ContainerRegistries; allowed != nil {
|
||||
dst.Spec.ContainerRegistries = allowed
|
||||
}
|
||||
|
||||
dst.Spec.NodeSelector = in.Spec.NodeSelector
|
||||
dst.Spec.NetworkPolicies = in.Spec.NetworkPolicies
|
||||
dst.Spec.LimitRanges = in.Spec.LimitRanges
|
||||
dst.Spec.ResourceQuota = in.Spec.ResourceQuota
|
||||
dst.Spec.AdditionalRoleBindings = in.Spec.AdditionalRoleBindings
|
||||
dst.Spec.ImagePullPolicies = in.Spec.ImagePullPolicies
|
||||
|
||||
if in.Spec.PriorityClasses != nil {
|
||||
dst.Spec.PriorityClasses = &in.Spec.PriorityClasses.AllowedListSpec
|
||||
}
|
||||
|
||||
if in.Spec.PreventDeletion {
|
||||
annotations[api.ProtectedTenantAnnotation] = "true" //nolint:goconst
|
||||
}
|
||||
|
||||
if in.Spec.Cordoned {
|
||||
annotations["capsule.clastix.io/cordon"] = "true"
|
||||
}
|
||||
|
||||
dst.SetAnnotations(annotations)
|
||||
|
||||
dst.Status.Size = in.Status.Size
|
||||
dst.Status.Namespaces = in.Status.Namespaces
|
||||
|
||||
switch in.Status.State {
|
||||
case TenantStateActive:
|
||||
dst.Status.State = capsulev1beta1.TenantStateActive
|
||||
case TenantStateCordoned:
|
||||
dst.Status.State = capsulev1beta1.TenantStateCordoned
|
||||
default:
|
||||
dst.Status.State = capsulev1beta1.TenantStateActive
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (in *Tenant) IsFull() bool {
|
||||
// we don't have limits on assigned Namespaces
|
||||
if in.Spec.NamespaceOptions == nil || in.Spec.NamespaceOptions.Quota == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(in.Status.Namespaces) >= int(*in.Spec.NamespaceOptions.Quota)
|
||||
}
|
||||
|
||||
func (in *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)
|
||||
|
||||
in.Status.Namespaces = l
|
||||
in.Status.Size = uint(len(l))
|
||||
}
|
||||
|
||||
func (in *Tenant) GetOwnerProxySettings(name string, kind OwnerKind) []ProxySettings {
|
||||
return in.Spec.Owners.FindOwner(name, kind).ProxyOperations
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
// +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"`
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/projectcapsule/capsule/pkg/api"
|
||||
)
|
||||
|
||||
// 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 *api.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.
|
||||
// A default value can be specified, and all the PersistentVolumeClaim resources created will inherit the declared class.
|
||||
// Optional.
|
||||
StorageClasses *api.DefaultAllowedListSpec `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 *api.AllowedListSpec `json:"containerRegistries,omitempty"`
|
||||
// Specifies the label to control the placement of pods on a given pool of worker nodes. All namespaces 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 api.NetworkPolicySpec `json:"networkPolicies,omitempty"`
|
||||
// Specifies the resource min/max usage restrictions to the Tenant. The assigned values are inherited by any namespace created in the Tenant. Optional.
|
||||
LimitRanges api.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 api.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 []api.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 []api.ImagePullPolicySpec `json:"imagePullPolicies,omitempty"`
|
||||
// Specifies the allowed RuntimeClasses assigned to the Tenant.
|
||||
// Capsule assures that all Pods resources created in the Tenant can use only one of the allowed RuntimeClasses.
|
||||
// Optional.
|
||||
RuntimeClasses *api.SelectorAllowedListSpec `json:"runtimeClasses,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.
|
||||
// A default value can be specified, and all the Pod resources created will inherit the declared class.
|
||||
// Optional.
|
||||
PriorityClasses *api.DefaultAllowedListSpec `json:"priorityClasses,omitempty"`
|
||||
// Toggling the Tenant resources cordoning, when enable resources cannot be deleted.
|
||||
Cordoned bool `json:"cordoned,omitempty"`
|
||||
// Prevent accidental deletion of the Tenant.
|
||||
// When enabled, the deletion request will be declined.
|
||||
PreventDeletion bool `json:"preventDeletion,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
// +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 (in *Tenant) GetNamespaces() (res []string) {
|
||||
res = make([]string, 0, len(in.Status.Namespaces))
|
||||
|
||||
res = append(res, in.Status.Namespaces...)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// +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{})
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright 2020-2023 Project Capsule Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1beta2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// GlobalTenantResourceSpec defines the desired state of GlobalTenantResource.
|
||||
type GlobalTenantResourceSpec struct {
|
||||
// Defines the Tenant selector used target the tenants on which resources must be propagated.
|
||||
TenantSelector metav1.LabelSelector `json:"tenantSelector,omitempty"`
|
||||
TenantResourceSpec `json:",inline"`
|
||||
}
|
||||
|
||||
// GlobalTenantResourceStatus defines the observed state of GlobalTenantResource.
|
||||
type GlobalTenantResourceStatus struct {
|
||||
// List of Tenants addressed by the GlobalTenantResource.
|
||||
SelectedTenants []string `json:"selectedTenants"`
|
||||
// List of the replicated resources for the given TenantResource.
|
||||
ProcessedItems ProcessedItems `json:"processedItems"`
|
||||
}
|
||||
|
||||
type ProcessedItems []ObjectReferenceStatus
|
||||
|
||||
func (p *ProcessedItems) AsSet() sets.Set[string] {
|
||||
set := sets.New[string]()
|
||||
|
||||
for _, i := range *p {
|
||||
set.Insert(i.String())
|
||||
}
|
||||
|
||||
return set
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:resource:scope=Cluster
|
||||
|
||||
// GlobalTenantResource allows to propagate resource replications to a specific subset of Tenant resources.
|
||||
type GlobalTenantResource struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec GlobalTenantResourceSpec `json:"spec,omitempty"`
|
||||
Status GlobalTenantResourceStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// GlobalTenantResourceList contains a list of GlobalTenantResource.
|
||||
type GlobalTenantResourceList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []GlobalTenantResource `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&GlobalTenantResource{}, &GlobalTenantResourceList{})
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user