mirror of
https://github.com/clastix/kamaji.git
synced 2026-03-02 01:30:43 +00:00
Compare commits
102 Commits
edge-24.9.
...
edge-25.3.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09a5b05a9c | ||
|
|
e2f4dd0dce | ||
|
|
7f7f649c7f | ||
|
|
e38979a443 | ||
|
|
c87d6ffc47 | ||
|
|
22a40409f2 | ||
|
|
e7df0f15d8 | ||
|
|
4f70df8b61 | ||
|
|
6a6c83a1c6 | ||
|
|
b0cbec9d3e | ||
|
|
3098279911 | ||
|
|
d1d092505b | ||
|
|
db2ccf1c9f | ||
|
|
e19c33337f | ||
|
|
b42ee8f1ad | ||
|
|
9d48eaecb3 | ||
|
|
f7eb53ccc0 | ||
|
|
d5ed4db445 | ||
|
|
38652260b5 | ||
|
|
b231575940 | ||
|
|
899da1aec4 | ||
|
|
3de661b4e6 | ||
|
|
2391286d4a | ||
|
|
1e9e6e497a | ||
|
|
34ff302ea2 | ||
|
|
54e6428715 | ||
|
|
2f5ba4820a | ||
|
|
c69a6079d1 | ||
|
|
c3d8b959e1 | ||
|
|
0c1c2535a8 | ||
|
|
c9547220bf | ||
|
|
abfc65a546 | ||
|
|
f238820c0d | ||
|
|
521fbf98bb | ||
|
|
dedfbb136b | ||
|
|
3b813ad02f | ||
|
|
339d6497ba | ||
|
|
3d0cfddefa | ||
|
|
e154611090 | ||
|
|
1ddaeb3aae | ||
|
|
d3580c8bc1 | ||
|
|
cb58ad680d | ||
|
|
3c5a7af78a | ||
|
|
b721dce799 | ||
|
|
1d72802abd | ||
|
|
f82350f17b | ||
|
|
68d581abc7 | ||
|
|
af2b55d47b | ||
|
|
f9a0436a42 | ||
|
|
4b90a67e4b | ||
|
|
9cbe3b1f2b | ||
|
|
f29e2195d3 | ||
|
|
8dd805712b | ||
|
|
ae7aa54e43 | ||
|
|
c1dd106680 | ||
|
|
21c299bdda | ||
|
|
0390fca416 | ||
|
|
7824b29df8 | ||
|
|
1556adb8fa | ||
|
|
813062f345 | ||
|
|
9171f46c60 | ||
|
|
6244f8c524 | ||
|
|
99784dfa47 | ||
|
|
b6e1b49ba4 | ||
|
|
378dfb9b9d | ||
|
|
2b17282b0e | ||
|
|
1c8c67b95b | ||
|
|
11e1e6c25b | ||
|
|
7904b4d04a | ||
|
|
aaad06870e | ||
|
|
305dc82de1 | ||
|
|
1a1f7c42d7 | ||
|
|
2fda2b0148 | ||
|
|
59fa575d20 | ||
|
|
b334ea59f1 | ||
|
|
792b118f79 | ||
|
|
e330690b7f | ||
|
|
c4a5b4a5fd | ||
|
|
303bc07c31 | ||
|
|
6b6370885a | ||
|
|
495890e165 | ||
|
|
0c0111094e | ||
|
|
fdd0035915 | ||
|
|
7c0eb8d41d | ||
|
|
1bfbca5e19 | ||
|
|
2b54d83a51 | ||
|
|
12248dea3d | ||
|
|
4e8c2b66c0 | ||
|
|
3b1020a8f3 | ||
|
|
986f2ed114 | ||
|
|
674923c036 | ||
|
|
f3c6a7a41e | ||
|
|
9ca69e91f9 | ||
|
|
e4939f6dcb | ||
|
|
fcad29ddba | ||
|
|
cae3c6041f | ||
|
|
7e08b9a7ce | ||
|
|
a21f199847 | ||
|
|
7b89d69a1c | ||
|
|
8b71843325 | ||
|
|
489e0e1653 | ||
|
|
71b653eee9 |
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -8,9 +8,12 @@ updates:
|
||||
commit-message:
|
||||
prefix: "feat(deps)"
|
||||
groups:
|
||||
arrow:
|
||||
k8s:
|
||||
patterns:
|
||||
- "k8s.io*"
|
||||
etcd:
|
||||
patterns:
|
||||
- "go.etcd.io/etcd/*"
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
|
||||
6
.github/workflows/ci.yaml
vendored
6
.github/workflows/ci.yaml
vendored
@@ -16,11 +16,11 @@ jobs:
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6.1.0
|
||||
uses: golangci/golangci-lint-action@v6.5.2
|
||||
with:
|
||||
version: v1.54.2
|
||||
version: v1.62.2
|
||||
only-new-issues: false
|
||||
args: --timeout 5m --config .golangci.yml
|
||||
args: --config .golangci.yml
|
||||
diff:
|
||||
name: diff
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
|
||||
linters-settings:
|
||||
revive:
|
||||
rules:
|
||||
- name: dot-imports
|
||||
arguments:
|
||||
- allowedPackages:
|
||||
- "github.com/onsi/ginkgo/v2"
|
||||
- "github.com/onsi/gomega"
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
@@ -13,16 +23,14 @@ linters:
|
||||
disable:
|
||||
- depguard
|
||||
- wrapcheck
|
||||
- gomnd
|
||||
- scopelint
|
||||
- mnd
|
||||
- varnamelen
|
||||
- testpackage
|
||||
- tagliatelle
|
||||
- paralleltest
|
||||
- ireturn
|
||||
- goerr113
|
||||
- err113
|
||||
- gochecknoglobals
|
||||
- exhaustivestruct
|
||||
- wsl
|
||||
- exhaustive
|
||||
- nosprintfhostport
|
||||
@@ -39,13 +47,7 @@ linters:
|
||||
- cyclop
|
||||
- gocognit
|
||||
- nestif
|
||||
- perfsprint
|
||||
# deprecated linters
|
||||
- deadcode
|
||||
- golint
|
||||
- interfacer
|
||||
- structcheck
|
||||
- varcheck
|
||||
- nosnakecase
|
||||
- ifshort
|
||||
- maligned
|
||||
- exportloopref
|
||||
enable-all: true
|
||||
|
||||
24
ADOPTERS.md
24
ADOPTERS.md
@@ -7,17 +7,25 @@ Feel free to open a Pull-Request to get yours listed.
|
||||
|
||||
| Type | Name | Since | Website | Use-Case |
|
||||
|:-|:-|:-|:-|:-|
|
||||
| Vendor | DCloud | 2024 | [link](https://dcloud.co.id) | DCloud is an Indonesian Cloud Provider using Kamaji to build and offer [Managed Kubernetes Service](https://dcloud.co.id/dkubes.html). |
|
||||
| End-user | Sicuro Tech Lab | 2024 | [link](https://sicurotechlab.it/) | Sicuro Tech Lab offers cloud infrastructure for Web Agencies and uses kamaji to provide managed k8s services. |
|
||||
| R&D | TIM | 2024 | [link](https://www.gruppotim.it) | TIM is an Italian telecommunications company using Kamaji for experimental research and development purposes. |
|
||||
| End-user | KINX | 2024 | [link](https://kinx.net/?lang=en) | KINX is an Internet infrastructure service provider and will use kamaji for its new [Managed Kubernetes Service](https://kinx.net/service/cloud/kubernetes/intro/?lang=en). |
|
||||
| End-user | sevensphere | 2023 | [link](https://www.sevensphere.io) | Sevensphere provides consulting services for end-user companies / cloud providers and uses Kamaji for designing cloud/on-premises Kubernetes-as-a-Service platform. |
|
||||
| Vendor | Ænix | 2023 | [link](https://aenix.io/) | Ænix provides consulting services for cloud providers and uses Kamaji for running Kubernetes-as-a-Service in free PaaS platform [Cozystack](https://cozystack.io). |
|
||||
| Vendor | Netsons | 2023 | [link](https://www.netsons.com) | Netsons is an Italian hosting and cloud provider and uses Kamaji in its [Managed Kubernetes](https://www.netsons.com/kubernetes) offering. |
|
||||
| Vendor | Aknostic | 2023 | [link](https://aknostic.com) | Aknostic is a cloud-native consultancy company using Kamaji to build a Kubernetes based PaaS. |
|
||||
| R&D | Aruba | 2024 | [link](https://www.aruba.it/home.aspx) | Aruba Cloud is an Italian Cloud Service Provider evaluating Kamaji to build and offer [Managed Kubernetes Service](https://my.arubacloud.com). |
|
||||
| Vendor | DCloud | 2024 | [link](https://dcloud.co.id) | DCloud is an Indonesian Cloud Provider using Kamaji to build and offer [Managed Kubernetes Service](https://dcloud.co.id/dkubes.html). |
|
||||
| Vendor | Dinova | 2025 | [link](https://dinova.one/) | Dinova is an Italian cloud services provider that integrates Kamaji in its datacenters to offer fully managed Kubernetes clusters. |
|
||||
| End-user | KINX | 2024 | [link](https://kinx.net/?lang=en) | KINX is an Internet infrastructure service provider and will use kamaji for its new [Managed Kubernetes Service](https://kinx.net/service/cloud/kubernetes/intro/?lang=en). |
|
||||
| Vendor | Netsons | 2023 | [link](https://www.netsons.com) | Netsons is an Italian hosting and cloud provider and uses Kamaji in its [Managed Kubernetes](https://www.netsons.com/kubernetes) offering. |
|
||||
| Vendor | NVIDIA | 2024 | [link](https://github.com/NVIDIA/doca-platform) | DOCA Platform Framework manages provisioning and service orchestration for NVIDIA Bluefield DPUs. |
|
||||
| R&D | Orange | 2024 | [link](https://gitlab.com/Orange-OpenSource/kanod) | Orange is a French telecommunications company using Kamaji for experimental research purpose, with Kanod research solution. |
|
||||
| Vendor | Platform9 | 2024 | [link](https://elasticmachinepool.com) | Platform9 uses Kamaji in its offering - Elastic Machine Pool, which is a tool for optimizing the cost of running kubernetes clusters in EKS. |
|
||||
| Vendor | Qumulus | 2024 | [link](https://www.qumulus.io) | Qumulus is a cloud provider and plans to use Kamaji for it's hosted Kubernetes service |
|
||||
|
||||
| End-user | sevensphere | 2023 | [link](https://www.sevensphere.io) | Sevensphere provides consulting services for end-user companies / cloud providers and uses Kamaji for designing cloud/on-premises Kubernetes-as-a-Service platform. |
|
||||
| End-user | Sicuro Tech Lab | 2024 | [link](https://sicurotechlab.it/) | Sicuro Tech Lab offers cloud infrastructure for Web Agencies and uses kamaji to provide managed k8s services. |
|
||||
| Vendor | Sovereign Cloud Stack | 2024 | [link](https://sovereigncloudstack.org) | Sovereign Cloud Stack develops a standardized cloud platform and uses Kamaji in there Kubernetes-as-a-Service reference implementation |
|
||||
| R&D | TIM | 2024 | [link](https://www.gruppotim.it) | TIM is an Italian telecommunications company using Kamaji for experimental research and development purposes. |
|
||||
| End-user | Tinext Cloud | 2025 | [link](https://cloud.tinext.com) | Tinex Cloud is a Swiss cloud service provider using Kamaji to build their Managed Kubernetes Services. |
|
||||
| Vendor | Ænix | 2023 | [link](https://aenix.io/) | Ænix provides consulting services for cloud providers and uses Kamaji for running Kubernetes-as-a-Service in free PaaS platform [Cozystack](https://cozystack.io). |
|
||||
| End-user | Rackspace | 2024 | [link](https://spot.rackspace.com/) | Rackspace Spot uses Kamaji to manage our instances, offering fully-managed kubernetes infrastructure, auctioned in an open market. |
|
||||
| R&D | IONOS Cloud | 2024 | [link](https://cloud.ionos.com/) | IONOS Cloud is a German Cloud Provider evaluating Kamaji for its [Managed Kubernetes platform](https://cloud.ionos.com/managed/kubernetes). |
|
||||
| Vendor | OVHCloud | 2025 | [link](https://www.ovhcloud.com/) | OVHCloud is a European Cloud Provider that will use Kamaji for its Managed Kubernetes Service offer. |
|
||||
|
||||
### Adopter Types
|
||||
|
||||
|
||||
40
Makefile
40
Makefile
@@ -3,7 +3,21 @@
|
||||
# To re-generate a bundle for another specific version without changing the standard setup, you can:
|
||||
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
|
||||
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
|
||||
VERSION ?= v1.0.0
|
||||
VERSION ?= $(or $(shell git describe --abbrev=0 --tags 2>/dev/null),$(GIT_HEAD_COMMIT))
|
||||
|
||||
# ENVTEST_K8S_VERSION specifies the Kubernetes version to be used
|
||||
# during testing with the envtest environment. This ensures that
|
||||
# the tests run against the correct API and behavior for the
|
||||
# specific Kubernetes release being targeted (v1.31.0 in this case).
|
||||
ENVTEST_K8S_VERSION = 1.31.0
|
||||
|
||||
# ENVTEST_VERSION defines the version of the setup-envtest binary
|
||||
# used to manage and download the Kubernetes binaries (like etcd,
|
||||
# kube-apiserver, and kubectl) required for testing. This version
|
||||
# ensures compatibility with the selected Kubernetes version and
|
||||
# must align closely with recent releases (release-0.19 is chosen here).
|
||||
# Mismatches between these versions could result in compatibility issues.
|
||||
ENVTEST_VERSION ?= release-0.19
|
||||
|
||||
# Image URL to use all building/pushing image targets
|
||||
CONTAINER_REPOSITORY ?= docker.io/clastix/kamaji
|
||||
@@ -34,6 +48,7 @@ HELM ?= $(LOCALBIN)/helm
|
||||
KIND ?= $(LOCALBIN)/kind
|
||||
KO ?= $(LOCALBIN)/ko
|
||||
YQ ?= $(LOCALBIN)/yq
|
||||
ENVTEST ?= $(LOCALBIN)/setup-envtest
|
||||
|
||||
all: build
|
||||
|
||||
@@ -88,13 +103,18 @@ $(CONTROLLER_GEN): $(LOCALBIN)
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
||||
$(GOLANGCI_LINT): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/golangci-lint || GOBIN=$(LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54.2
|
||||
test -s $(LOCALBIN)/golangci-lint || GOBIN=$(LOCALBIN) go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.2
|
||||
|
||||
.PHONY: apidocs-gen
|
||||
apidocs-gen: $(APIDOCS_GEN) ## Download crdoc locally if necessary.
|
||||
$(APIDOCS_GEN): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/crdoc || GOBIN=$(LOCALBIN) go install fybrik.io/crdoc@latest
|
||||
|
||||
.PHONY: envtest
|
||||
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
|
||||
$(ENVTEST): $(LOCALBIN)
|
||||
test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@$(ENVTEST_VERSION)
|
||||
|
||||
##@ Development
|
||||
|
||||
rbac: controller-gen yq
|
||||
@@ -121,8 +141,14 @@ generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and
|
||||
golint: golangci-lint ## Linting the code according to the styling guide.
|
||||
$(GOLANGCI_LINT) run -c .golangci.yml
|
||||
|
||||
test:
|
||||
go test ./... -coverprofile cover.out
|
||||
## Run unit tests (all tests except E2E).
|
||||
.PHONY: test
|
||||
test: envtest ginkgo
|
||||
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" $(GINKGO) -r -v --trace \
|
||||
./api/... \
|
||||
./cmd/... \
|
||||
./internal/... \
|
||||
-coverprofile cover.out
|
||||
|
||||
_datastore-mysql:
|
||||
$(MAKE) NAME=$(NAME) -C deploy/kine/mysql mariadb
|
||||
@@ -203,8 +229,8 @@ metallb:
|
||||
cat hack/metallb.yaml | sed -E "s|172.19|$$(docker network inspect -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}' kind | sed -E 's|^([0-9]+\.[0-9]+)\..*$$|\1|g')|g" | kubectl apply -f -
|
||||
|
||||
cert-manager:
|
||||
$(HELM) repo add bitnami https://charts.bitnami.com/bitnami
|
||||
$(HELM) upgrade --install cert-manager bitnami/cert-manager --namespace certmanager-system --create-namespace --set "installCRDs=true"
|
||||
$(HELM) repo add jetstack https://charts.jetstack.io
|
||||
$(HELM) upgrade --install cert-manager jetstack/cert-manager --namespace certmanager-system --create-namespace --set "installCRDs=true"
|
||||
|
||||
load: kind
|
||||
$(KIND) load docker-image --name kamaji ${CONTAINER_REPOSITORY}:${VERSION}
|
||||
@@ -219,7 +245,7 @@ env:
|
||||
e2e: env build load helm ginkgo cert-manager ## Create a KinD cluster, install Kamaji on it and run the test suite.
|
||||
$(HELM) repo add clastix https://clastix.github.io/charts
|
||||
$(HELM) dependency build ./charts/kamaji
|
||||
$(HELM) upgrade --debug --install kamaji ./charts/kamaji --create-namespace --namespace kamaji-system --set "image.pullPolicy=Never" --set "telemetry.disabled=true"
|
||||
$(HELM) upgrade --debug --install kamaji ./charts/kamaji --create-namespace --namespace kamaji-system --set "image.tag=$(VERSION)" --set "image.pullPolicy=Never" --set "telemetry.disabled=true"
|
||||
$(MAKE) datastores
|
||||
$(GINKGO) -v ./e2e
|
||||
|
||||
|
||||
55
api/v1alpha1/suite_test.go
Normal file
55
api/v1alpha1/suite_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
)
|
||||
|
||||
var (
|
||||
cfg *rest.Config
|
||||
k8sClient client.Client
|
||||
testEnv *envtest.Environment
|
||||
)
|
||||
|
||||
func TestAPIs(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "TenantControlPlane Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
By("bootstrapping test environment")
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{
|
||||
filepath.Join("..", "..", "charts", "kamaji", "crds"),
|
||||
// filepath.Join("../..", "chart", "kamaji", "crds"),
|
||||
},
|
||||
}
|
||||
|
||||
var err error
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(cfg).ToNot(BeNil())
|
||||
|
||||
err = AddToScheme(scheme.Scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(k8sClient).ToNot(BeNil())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
By("tearing down the test environment")
|
||||
err := testEnv.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
@@ -8,5 +8,5 @@ package v1alpha1
|
||||
// +kubebuilder:object:generate=false
|
||||
type KubeadmConfigChecksumDependant interface {
|
||||
GetChecksum() string
|
||||
SetChecksum(string)
|
||||
SetChecksum(checksum string)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func (in KubeadmPhaseStatus) GetChecksum() string {
|
||||
func (in *KubeadmPhaseStatus) GetChecksum() string {
|
||||
return in.Checksum
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,27 @@ import (
|
||||
|
||||
// NetworkProfileSpec defines the desired state of NetworkProfile.
|
||||
type NetworkProfileSpec struct {
|
||||
// LoadBalancerSourceRanges restricts the IP ranges that can access
|
||||
// the LoadBalancer type Service. This field defines a list of IP
|
||||
// address ranges (in CIDR format) that are allowed to access the service.
|
||||
// If left empty, the service will allow traffic from all IP ranges (0.0.0.0/0).
|
||||
// This feature is useful for restricting access to API servers or services
|
||||
// to specific networks for security purposes.
|
||||
// Example: {"192.168.1.0/24", "10.0.0.0/8"}
|
||||
LoadBalancerSourceRanges []string `json:"loadBalancerSourceRanges,omitempty"`
|
||||
// Specify the LoadBalancer class in case of multiple load balancer implementations.
|
||||
// Field supported only for Tenant Control Plane instances exposed using a LoadBalancer Service.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="LoadBalancerClass is immutable"
|
||||
LoadBalancerClass *string `json:"loadBalancerClass,omitempty"`
|
||||
// Address where API server of will be exposed.
|
||||
// In case of LoadBalancer Service, this can be empty in order to use the exposed IP provided by the cloud controller manager.
|
||||
Address string `json:"address,omitempty"`
|
||||
// The default domain name used for DNS resolution within the cluster.
|
||||
//+kubebuilder:default="cluster.local"
|
||||
//+kubebuilder:validation:XValidation:rule="self == oldSelf",message="changing the cluster domain is not supported"
|
||||
//+kubebuilder:validation:Pattern=.*\..*
|
||||
ClusterDomain string `json:"clusterDomain,omitempty"`
|
||||
// AllowAddressAsExternalIP will include tenantControlPlane.Spec.NetworkProfile.Address in the section of
|
||||
// ExternalIPs of the Kubernetes Service (only ClusterIP or NodePort)
|
||||
AllowAddressAsExternalIP bool `json:"allowAddressAsExternalIP,omitempty"`
|
||||
@@ -23,13 +41,16 @@ type NetworkProfileSpec struct {
|
||||
// CertSANs sets extra Subject Alternative Names (SANs) for the API Server signing certificate.
|
||||
// Use this field to add additional hostnames when exposing the Tenant Control Plane with third solutions.
|
||||
CertSANs []string `json:"certSANs,omitempty"`
|
||||
// Kubernetes Service
|
||||
// CIDR for Kubernetes Services: if empty, defaulted to 10.96.0.0/16.
|
||||
//+kubebuilder:default="10.96.0.0/16"
|
||||
ServiceCIDR string `json:"serviceCidr,omitempty"`
|
||||
// CIDR for Kubernetes Pods
|
||||
// CIDR for Kubernetes Pods: if empty, defaulted to 10.244.0.0/16.
|
||||
//+kubebuilder:default="10.244.0.0/16"
|
||||
PodCIDR string `json:"podCidr,omitempty"`
|
||||
//+kubebuilder:default={"10.96.0.10"}
|
||||
// The DNS Service for internal resolution, it must match the Service CIDR.
|
||||
// In case of an empty value, it is automatically computed according to the Service CIDR, e.g.:
|
||||
// Service CIDR 10.96.0.0/16, the resulting DNS Service IP will be 10.96.0.10 for IPv4,
|
||||
// for IPv6 from the CIDR 2001:db8:abcd::/64 the resulting DNS Service IP will be 2001:db8:abcd::10.
|
||||
DNSServiceIPs []string `json:"dnsServiceIPs,omitempty"`
|
||||
}
|
||||
|
||||
@@ -249,12 +270,27 @@ type AddonsSpec struct {
|
||||
}
|
||||
|
||||
// TenantControlPlaneSpec defines the desired state of TenantControlPlane.
|
||||
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.dataStore) || has(self.dataStore)", message="unsetting the dataStore is not supported"
|
||||
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.dataStoreSchema) || has(self.dataStoreSchema)", message="unsetting the dataStoreSchema is not supported"
|
||||
// +kubebuilder:validation:XValidation:rule="!has(self.networkProfile.loadBalancerSourceRanges) || (size(self.networkProfile.loadBalancerSourceRanges) == 0 || self.controlPlane.service.serviceType == 'LoadBalancer')", message="LoadBalancer source ranges are supported only with LoadBalancer service type"
|
||||
// +kubebuilder:validation:XValidation:rule="!has(self.networkProfile.loadBalancerClass) || self.controlPlane.service.serviceType == 'LoadBalancer'", message="LoadBalancerClass is supported only with LoadBalancer service type"
|
||||
// +kubebuilder:validation:XValidation:rule="self.controlPlane.service.serviceType != 'LoadBalancer' || (oldSelf.controlPlane.service.serviceType != 'LoadBalancer' && self.controlPlane.service.serviceType == 'LoadBalancer') || has(self.networkProfile.loadBalancerClass) == has(oldSelf.networkProfile.loadBalancerClass)",message="LoadBalancerClass cannot be set or unset at runtime"
|
||||
|
||||
type TenantControlPlaneSpec struct {
|
||||
// DataStore allows to specify a DataStore that should be used to store the Kubernetes data for the given Tenant Control Plane.
|
||||
// This parameter is optional and acts as an override over the default one which is used by the Kamaji Operator.
|
||||
// Migration from a different DataStore to another one is not yet supported and the reconciliation will be blocked.
|
||||
DataStore string `json:"dataStore,omitempty"`
|
||||
ControlPlane ControlPlane `json:"controlPlane"`
|
||||
// DataStore specifies the DataStore that should be used to store the Kubernetes data for the given Tenant Control Plane.
|
||||
// When Kamaji runs with the default DataStore flag, all empty values will inherit the default value.
|
||||
// By leaving it empty and running Kamaji with no default DataStore flag, it is possible to achieve automatic assignment to a specific DataStore object.
|
||||
//
|
||||
// Migration from one DataStore to another backed by the same Driver is possible. See: https://kamaji.clastix.io/guides/datastore-migration/
|
||||
// Migration from one DataStore to another backed by a different Driver is not supported.
|
||||
DataStore string `json:"dataStore,omitempty"`
|
||||
// DataStoreSchema allows to specify the name of the database (for relational DataStores) or the key prefix (for etcd). This
|
||||
// value is optional and immutable. Note that Kamaji currently doesn't ensure that DataStoreSchema values are unique. It's up
|
||||
// to the user to avoid clashes between different TenantControlPlanes. If not set upon creation, Kamaji will default the
|
||||
// DataStoreSchema by concatenating the namespace and name of the TenantControlPlane.
|
||||
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="changing the dataStoreSchema is not supported"
|
||||
DataStoreSchema string `json:"dataStoreSchema,omitempty"`
|
||||
ControlPlane ControlPlane `json:"controlPlane"`
|
||||
// Kubernetes specification for tenant control plane
|
||||
Kubernetes KubernetesSpec `json:"kubernetes"`
|
||||
// NetworkProfile specifies how the network is
|
||||
|
||||
78
api/v1alpha1/tenantcontrolplane_types_test.go
Normal file
78
api/v1alpha1/tenantcontrolplane_types_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
var _ = Describe("Cluster controller", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
tcp *TenantControlPlane
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = context.Background() //nolint:fatcontext
|
||||
tcp = &TenantControlPlane{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tcp",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: TenantControlPlaneSpec{},
|
||||
}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
if err := k8sClient.Delete(ctx, tcp); err != nil && !apierrors.IsNotFound(err) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
})
|
||||
|
||||
Context("LoadBalancer Source Ranges", func() {
|
||||
It("allows creation when no CIDR ranges are provided", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = ServiceTypeLoadBalancer
|
||||
|
||||
err := k8sClient.Create(ctx, tcp)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("allows creation with an explicitly empty CIDR list", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = ServiceTypeLoadBalancer
|
||||
tcp.Spec.NetworkProfile.LoadBalancerSourceRanges = []string{}
|
||||
|
||||
err := k8sClient.Create(ctx, tcp)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("allows creation when service type is not LoadBalancer and it has an empty CIDR list", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = ServiceTypeNodePort
|
||||
|
||||
err := k8sClient.Create(ctx, tcp)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("allows CIDR ranges when service type is LoadBalancer", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = ServiceTypeLoadBalancer
|
||||
tcp.Spec.NetworkProfile.LoadBalancerSourceRanges = []string{"192.168.0.0/24"}
|
||||
|
||||
err := k8sClient.Create(ctx, tcp)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("denies CIDR ranges when service type is not LoadBalancer", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = ServiceTypeNodePort
|
||||
tcp.Spec.NetworkProfile.LoadBalancerSourceRanges = []string{"192.168.0.0/24"}
|
||||
|
||||
err := k8sClient.Create(ctx, tcp)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("LoadBalancer source ranges are supported only with LoadBalancer service type"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1105,6 +1105,16 @@ func (in *KubernetesVersion) DeepCopy() *KubernetesVersion {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkProfileSpec) DeepCopyInto(out *NetworkProfileSpec) {
|
||||
*out = *in
|
||||
if in.LoadBalancerSourceRanges != nil {
|
||||
in, out := &in.LoadBalancerSourceRanges, &out.LoadBalancerSourceRanges
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.LoadBalancerClass != nil {
|
||||
in, out := &in.LoadBalancerClass, &out.LoadBalancerClass
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.CertSANs != nil {
|
||||
in, out := &in.CertSANs, &out.CertSANs
|
||||
*out = make([]string, len(*in))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
dependencies:
|
||||
- name: kamaji-etcd
|
||||
repository: https://clastix.github.io/charts
|
||||
version: 0.8.0
|
||||
digest: sha256:525b0eb2b5bae709d62de9328312d42c54b5219c6df67061de0da79eeca04fb3
|
||||
generated: "2024-08-25T08:44:24.92211307+02:00"
|
||||
version: 0.9.2
|
||||
digest: sha256:ba76d3a30e5e20dbbbbcc36a0e7465d4b1adacc956061e7f6ea47b99fc8f08a6
|
||||
generated: "2025-03-14T21:23:30.421915+09:00"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v2
|
||||
appVersion: v1.0.0
|
||||
appVersion: v0.0.0
|
||||
description: Kamaji is the Hosted Control Plane Manager for Kubernetes.
|
||||
home: https://github.com/clastix/kamaji
|
||||
icon: https://github.com/clastix/kamaji/raw/master/assets/logo-colored.png
|
||||
@@ -17,11 +17,11 @@ name: kamaji
|
||||
sources:
|
||||
- https://github.com/clastix/kamaji
|
||||
type: application
|
||||
version: 2.0.0
|
||||
version: 0.0.0
|
||||
dependencies:
|
||||
- name: kamaji-etcd
|
||||
repository: https://clastix.github.io/charts
|
||||
version: ">=0.7.0"
|
||||
version: ">=0.9.2"
|
||||
condition: kamaji-etcd.deploy
|
||||
annotations:
|
||||
catalog.cattle.io/certified: partner
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# kamaji
|
||||
|
||||
  
|
||||
  
|
||||
|
||||
Kamaji is the Hosted Control Plane Manager for Kubernetes.
|
||||
|
||||
@@ -22,7 +22,7 @@ Kubernetes: `>=1.21.0-0`
|
||||
|
||||
| Repository | Name | Version |
|
||||
|------------|------|---------|
|
||||
| https://clastix.github.io/charts | kamaji-etcd | >=0.7.0 |
|
||||
| https://clastix.github.io/charts | kamaji-etcd | >=0.9.2 |
|
||||
|
||||
[Kamaji](https://github.com/clastix/kamaji) requires a [multi-tenant `etcd`](https://github.com/clastix/kamaji-internal/blob/master/deploy/getting-started-with-kamaji.md#setup-internal-multi-tenant-etcd) cluster.
|
||||
This Helm Chart starting from v0.1.1 provides the installation of an internal `etcd` in order to streamline the local test. If you'd like to use an externally managed etcd instance, you can specify the overrides and by setting the value `etcd.deploy=false`.
|
||||
@@ -31,9 +31,13 @@ This Helm Chart starting from v0.1.1 provides the installation of an internal `e
|
||||
|
||||
## Install Kamaji
|
||||
|
||||
To add clastix helm repository:
|
||||
|
||||
helm repo add clastix https://clastix.github.io/charts
|
||||
|
||||
To install the Chart with the release name `kamaji`:
|
||||
|
||||
helm upgrade --install --namespace kamaji-system --create-namespace clastix/kamaji
|
||||
helm upgrade --install --namespace kamaji-system --create-namespace kamaji clastix/kamaji
|
||||
|
||||
Show the status:
|
||||
|
||||
@@ -70,7 +74,7 @@ Here the values you can override:
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| affinity | object | `{}` | Kubernetes affinity rules to apply to Kamaji controller pods |
|
||||
| defaultDatastoreName | string | `"default"` | Specify the default DataStore name for the Kamaji instance. |
|
||||
| defaultDatastoreName | string | `"default"` | If specified, all the Kamaji instances with an unassigned DataStore will inherit this default value. |
|
||||
| extraArgs | list | `[]` | A list of extra arguments to add to the kamaji controller default ones |
|
||||
| fullnameOverride | string | `""` | |
|
||||
| healthProbeBindAddress | string | `":8081"` | The address the probe endpoint binds to. (default ":8081") |
|
||||
|
||||
@@ -18,10 +18,15 @@ This Helm Chart starting from v0.1.1 provides the installation of an internal `e
|
||||
|
||||
## Install Kamaji
|
||||
|
||||
To add clastix helm repository:
|
||||
|
||||
|
||||
helm repo add clastix https://clastix.github.io/charts
|
||||
|
||||
To install the Chart with the release name `kamaji`:
|
||||
|
||||
|
||||
helm upgrade --install --namespace kamaji-system --create-namespace clastix/kamaji
|
||||
helm upgrade --install --namespace kamaji-system --create-namespace kamaji clastix/kamaji
|
||||
|
||||
Show the status:
|
||||
|
||||
|
||||
@@ -66,7 +66,6 @@ spec:
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: TenantControlPlaneSpec defines the desired state of TenantControlPlane.
|
||||
properties:
|
||||
addons:
|
||||
description: Addons contain which addons are enabled
|
||||
@@ -498,7 +497,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -513,7 +512,7 @@ spec:
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -560,7 +559,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
sleep:
|
||||
description: Sleep represents the duration that the container should sleep before being terminated.
|
||||
description: Sleep represents a duration that the container should sleep.
|
||||
properties:
|
||||
seconds:
|
||||
description: Seconds is the number of seconds to sleep.
|
||||
@@ -572,8 +571,8 @@ spec:
|
||||
tcpSocket:
|
||||
description: |-
|
||||
Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept
|
||||
for the backward compatibility. There are no validation of this field and
|
||||
lifecycle hooks will fail in runtime when tcp handler is specified.
|
||||
for backward compatibility. There is no validation of this field and
|
||||
lifecycle hooks will fail at runtime when it is specified.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -604,7 +603,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -619,7 +618,7 @@ spec:
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -666,7 +665,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
sleep:
|
||||
description: Sleep represents the duration that the container should sleep before being terminated.
|
||||
description: Sleep represents a duration that the container should sleep.
|
||||
properties:
|
||||
seconds:
|
||||
description: Seconds is the number of seconds to sleep.
|
||||
@@ -678,8 +677,8 @@ spec:
|
||||
tcpSocket:
|
||||
description: |-
|
||||
Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept
|
||||
for the backward compatibility. There are no validation of this field and
|
||||
lifecycle hooks will fail in runtime when tcp handler is specified.
|
||||
for backward compatibility. There is no validation of this field and
|
||||
lifecycle hooks will fail at runtime when it is specified.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -706,7 +705,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -727,7 +726,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
grpc:
|
||||
description: GRPC specifies an action involving a GRPC port.
|
||||
description: GRPC specifies a GRPC HealthCheckRequest.
|
||||
properties:
|
||||
port:
|
||||
description: Port number of the gRPC service. Number must be in the range 1 to 65535.
|
||||
@@ -745,7 +744,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -810,7 +809,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
tcpSocket:
|
||||
description: TCPSocket specifies an action involving a TCP port.
|
||||
description: TCPSocket specifies a connection to a TCP port.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -912,7 +911,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -933,7 +932,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
grpc:
|
||||
description: GRPC specifies an action involving a GRPC port.
|
||||
description: GRPC specifies a GRPC HealthCheckRequest.
|
||||
properties:
|
||||
port:
|
||||
description: Port number of the gRPC service. Number must be in the range 1 to 65535.
|
||||
@@ -951,7 +950,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -1016,7 +1015,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
tcpSocket:
|
||||
description: TCPSocket specifies an action involving a TCP port.
|
||||
description: TCPSocket specifies a connection to a TCP port.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -1355,7 +1354,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -1376,7 +1375,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
grpc:
|
||||
description: GRPC specifies an action involving a GRPC port.
|
||||
description: GRPC specifies a GRPC HealthCheckRequest.
|
||||
properties:
|
||||
port:
|
||||
description: Port number of the gRPC service. Number must be in the range 1 to 65535.
|
||||
@@ -1394,7 +1393,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -1459,7 +1458,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
tcpSocket:
|
||||
description: TCPSocket specifies an action involving a TCP port.
|
||||
description: TCPSocket specifies a connection to a TCP port.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -1863,7 +1862,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -1878,7 +1877,7 @@ spec:
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -1925,7 +1924,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
sleep:
|
||||
description: Sleep represents the duration that the container should sleep before being terminated.
|
||||
description: Sleep represents a duration that the container should sleep.
|
||||
properties:
|
||||
seconds:
|
||||
description: Seconds is the number of seconds to sleep.
|
||||
@@ -1937,8 +1936,8 @@ spec:
|
||||
tcpSocket:
|
||||
description: |-
|
||||
Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept
|
||||
for the backward compatibility. There are no validation of this field and
|
||||
lifecycle hooks will fail in runtime when tcp handler is specified.
|
||||
for backward compatibility. There is no validation of this field and
|
||||
lifecycle hooks will fail at runtime when it is specified.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -1969,7 +1968,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -1984,7 +1983,7 @@ spec:
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -2031,7 +2030,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
sleep:
|
||||
description: Sleep represents the duration that the container should sleep before being terminated.
|
||||
description: Sleep represents a duration that the container should sleep.
|
||||
properties:
|
||||
seconds:
|
||||
description: Seconds is the number of seconds to sleep.
|
||||
@@ -2043,8 +2042,8 @@ spec:
|
||||
tcpSocket:
|
||||
description: |-
|
||||
Deprecated. TCPSocket is NOT supported as a LifecycleHandler and kept
|
||||
for the backward compatibility. There are no validation of this field and
|
||||
lifecycle hooks will fail in runtime when tcp handler is specified.
|
||||
for backward compatibility. There is no validation of this field and
|
||||
lifecycle hooks will fail at runtime when it is specified.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -2071,7 +2070,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -2092,7 +2091,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
grpc:
|
||||
description: GRPC specifies an action involving a GRPC port.
|
||||
description: GRPC specifies a GRPC HealthCheckRequest.
|
||||
properties:
|
||||
port:
|
||||
description: Port number of the gRPC service. Number must be in the range 1 to 65535.
|
||||
@@ -2110,7 +2109,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -2175,7 +2174,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
tcpSocket:
|
||||
description: TCPSocket specifies an action involving a TCP port.
|
||||
description: TCPSocket specifies a connection to a TCP port.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -2277,7 +2276,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -2298,7 +2297,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
grpc:
|
||||
description: GRPC specifies an action involving a GRPC port.
|
||||
description: GRPC specifies a GRPC HealthCheckRequest.
|
||||
properties:
|
||||
port:
|
||||
description: Port number of the gRPC service. Number must be in the range 1 to 65535.
|
||||
@@ -2316,7 +2315,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -2381,7 +2380,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
tcpSocket:
|
||||
description: TCPSocket specifies an action involving a TCP port.
|
||||
description: TCPSocket specifies a connection to a TCP port.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -2720,7 +2719,7 @@ spec:
|
||||
More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
|
||||
properties:
|
||||
exec:
|
||||
description: Exec specifies the action to take.
|
||||
description: Exec specifies a command to execute in the container.
|
||||
properties:
|
||||
command:
|
||||
description: |-
|
||||
@@ -2741,7 +2740,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
grpc:
|
||||
description: GRPC specifies an action involving a GRPC port.
|
||||
description: GRPC specifies a GRPC HealthCheckRequest.
|
||||
properties:
|
||||
port:
|
||||
description: Port number of the gRPC service. Number must be in the range 1 to 65535.
|
||||
@@ -2759,7 +2758,7 @@ spec:
|
||||
- port
|
||||
type: object
|
||||
httpGet:
|
||||
description: HTTPGet specifies the http request to perform.
|
||||
description: HTTPGet specifies an HTTP GET request to perform.
|
||||
properties:
|
||||
host:
|
||||
description: |-
|
||||
@@ -2824,7 +2823,7 @@ spec:
|
||||
format: int32
|
||||
type: integer
|
||||
tcpSocket:
|
||||
description: TCPSocket specifies an action involving a TCP port.
|
||||
description: TCPSocket specifies a connection to a TCP port.
|
||||
properties:
|
||||
host:
|
||||
description: 'Optional: Host name to connect to, defaults to the pod IP.'
|
||||
@@ -3215,6 +3214,8 @@ spec:
|
||||
description: |-
|
||||
awsElasticBlockStore represents an AWS Disk resource that is attached to a
|
||||
kubelet's host machine and then exposed to the pod.
|
||||
Deprecated: AWSElasticBlockStore is deprecated. All operations for the in-tree
|
||||
awsElasticBlockStore type are redirected to the ebs.csi.aws.com CSI driver.
|
||||
More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore
|
||||
properties:
|
||||
fsType:
|
||||
@@ -3246,7 +3247,10 @@ spec:
|
||||
- volumeID
|
||||
type: object
|
||||
azureDisk:
|
||||
description: azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
|
||||
description: |-
|
||||
azureDisk represents an Azure Data Disk mount on the host and bind mount to the pod.
|
||||
Deprecated: AzureDisk is deprecated. All operations for the in-tree azureDisk type
|
||||
are redirected to the disk.csi.azure.com CSI driver.
|
||||
properties:
|
||||
cachingMode:
|
||||
description: 'cachingMode is the Host Caching mode: None, Read Only, Read Write.'
|
||||
@@ -3278,7 +3282,10 @@ spec:
|
||||
- diskURI
|
||||
type: object
|
||||
azureFile:
|
||||
description: azureFile represents an Azure File Service mount on the host and bind mount to the pod.
|
||||
description: |-
|
||||
azureFile represents an Azure File Service mount on the host and bind mount to the pod.
|
||||
Deprecated: AzureFile is deprecated. All operations for the in-tree azureFile type
|
||||
are redirected to the file.csi.azure.com CSI driver.
|
||||
properties:
|
||||
readOnly:
|
||||
description: |-
|
||||
@@ -3296,7 +3303,9 @@ spec:
|
||||
- shareName
|
||||
type: object
|
||||
cephfs:
|
||||
description: cephFS represents a Ceph FS mount on the host that shares a pod's lifetime
|
||||
description: |-
|
||||
cephFS represents a Ceph FS mount on the host that shares a pod's lifetime.
|
||||
Deprecated: CephFS is deprecated and the in-tree cephfs type is no longer supported.
|
||||
properties:
|
||||
monitors:
|
||||
description: |-
|
||||
@@ -3347,6 +3356,8 @@ spec:
|
||||
cinder:
|
||||
description: |-
|
||||
cinder represents a cinder volume attached and mounted on kubelets host machine.
|
||||
Deprecated: Cinder is deprecated. All operations for the in-tree cinder type
|
||||
are redirected to the cinder.csi.openstack.org CSI driver.
|
||||
More info: https://examples.k8s.io/mysql-cinder-pd/README.md
|
||||
properties:
|
||||
fsType:
|
||||
@@ -3453,7 +3464,7 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
csi:
|
||||
description: csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers (Beta feature).
|
||||
description: csi (Container Storage Interface) represents ephemeral storage that is handled by certain external CSI drivers.
|
||||
properties:
|
||||
driver:
|
||||
description: |-
|
||||
@@ -3895,6 +3906,7 @@ spec:
|
||||
description: |-
|
||||
flexVolume represents a generic volume resource that is
|
||||
provisioned/attached using an exec based plugin.
|
||||
Deprecated: FlexVolume is deprecated. Consider using a CSIDriver instead.
|
||||
properties:
|
||||
driver:
|
||||
description: driver is the name of the driver to use for this volume.
|
||||
@@ -3938,7 +3950,9 @@ spec:
|
||||
- driver
|
||||
type: object
|
||||
flocker:
|
||||
description: flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running
|
||||
description: |-
|
||||
flocker represents a Flocker volume attached to a kubelet's host machine. This depends on the Flocker control service being running.
|
||||
Deprecated: Flocker is deprecated and the in-tree flocker type is no longer supported.
|
||||
properties:
|
||||
datasetName:
|
||||
description: |-
|
||||
@@ -3953,6 +3967,8 @@ spec:
|
||||
description: |-
|
||||
gcePersistentDisk represents a GCE Disk resource that is attached to a
|
||||
kubelet's host machine and then exposed to the pod.
|
||||
Deprecated: GCEPersistentDisk is deprecated. All operations for the in-tree
|
||||
gcePersistentDisk type are redirected to the pd.csi.storage.gke.io CSI driver.
|
||||
More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk
|
||||
properties:
|
||||
fsType:
|
||||
@@ -3988,7 +4004,7 @@ spec:
|
||||
gitRepo:
|
||||
description: |-
|
||||
gitRepo represents a git repository at a particular revision.
|
||||
DEPRECATED: GitRepo is deprecated. To provision a container with a git repo, mount an
|
||||
Deprecated: GitRepo is deprecated. To provision a container with a git repo, mount an
|
||||
EmptyDir into an InitContainer that clones the repo using git, then mount the EmptyDir
|
||||
into the Pod's container.
|
||||
properties:
|
||||
@@ -4011,6 +4027,7 @@ spec:
|
||||
glusterfs:
|
||||
description: |-
|
||||
glusterfs represents a Glusterfs mount on the host that shares a pod's lifetime.
|
||||
Deprecated: Glusterfs is deprecated and the in-tree glusterfs type is no longer supported.
|
||||
More info: https://examples.k8s.io/volumes/glusterfs/README.md
|
||||
properties:
|
||||
endpoints:
|
||||
@@ -4217,7 +4234,9 @@ spec:
|
||||
- claimName
|
||||
type: object
|
||||
photonPersistentDisk:
|
||||
description: photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine
|
||||
description: |-
|
||||
photonPersistentDisk represents a PhotonController persistent disk attached and mounted on kubelets host machine.
|
||||
Deprecated: PhotonPersistentDisk is deprecated and the in-tree photonPersistentDisk type is no longer supported.
|
||||
properties:
|
||||
fsType:
|
||||
description: |-
|
||||
@@ -4232,7 +4251,11 @@ spec:
|
||||
- pdID
|
||||
type: object
|
||||
portworxVolume:
|
||||
description: portworxVolume represents a portworx volume attached and mounted on kubelets host machine
|
||||
description: |-
|
||||
portworxVolume represents a portworx volume attached and mounted on kubelets host machine.
|
||||
Deprecated: PortworxVolume is deprecated. All operations for the in-tree portworxVolume type
|
||||
are redirected to the pxd.portworx.com CSI driver when the CSIMigrationPortworx feature-gate
|
||||
is on.
|
||||
properties:
|
||||
fsType:
|
||||
description: |-
|
||||
@@ -4567,7 +4590,9 @@ spec:
|
||||
x-kubernetes-list-type: atomic
|
||||
type: object
|
||||
quobyte:
|
||||
description: quobyte represents a Quobyte mount on the host that shares a pod's lifetime
|
||||
description: |-
|
||||
quobyte represents a Quobyte mount on the host that shares a pod's lifetime.
|
||||
Deprecated: Quobyte is deprecated and the in-tree quobyte type is no longer supported.
|
||||
properties:
|
||||
group:
|
||||
description: |-
|
||||
@@ -4605,6 +4630,7 @@ spec:
|
||||
rbd:
|
||||
description: |-
|
||||
rbd represents a Rados Block Device mount on the host that shares a pod's lifetime.
|
||||
Deprecated: RBD is deprecated and the in-tree rbd type is no longer supported.
|
||||
More info: https://examples.k8s.io/volumes/rbd/README.md
|
||||
properties:
|
||||
fsType:
|
||||
@@ -4677,7 +4703,9 @@ spec:
|
||||
- monitors
|
||||
type: object
|
||||
scaleIO:
|
||||
description: scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
description: |-
|
||||
scaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
|
||||
Deprecated: ScaleIO is deprecated and the in-tree scaleIO type is no longer supported.
|
||||
properties:
|
||||
fsType:
|
||||
default: xfs
|
||||
@@ -4803,7 +4831,9 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
storageos:
|
||||
description: storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
|
||||
description: |-
|
||||
storageOS represents a StorageOS volume attached and mounted on Kubernetes nodes.
|
||||
Deprecated: StorageOS is deprecated and the in-tree storageos type is no longer supported.
|
||||
properties:
|
||||
fsType:
|
||||
description: |-
|
||||
@@ -4848,7 +4878,10 @@ spec:
|
||||
type: string
|
||||
type: object
|
||||
vsphereVolume:
|
||||
description: vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine
|
||||
description: |-
|
||||
vsphereVolume represents a vSphere volume attached and mounted on kubelets host machine.
|
||||
Deprecated: VsphereVolume is deprecated. All operations for the in-tree vsphereVolume type
|
||||
are redirected to the csi.vsphere.vmware.com CSI driver.
|
||||
properties:
|
||||
fsType:
|
||||
description: |-
|
||||
@@ -6413,10 +6446,23 @@ spec:
|
||||
type: object
|
||||
dataStore:
|
||||
description: |-
|
||||
DataStore allows to specify a DataStore that should be used to store the Kubernetes data for the given Tenant Control Plane.
|
||||
This parameter is optional and acts as an override over the default one which is used by the Kamaji Operator.
|
||||
Migration from a different DataStore to another one is not yet supported and the reconciliation will be blocked.
|
||||
DataStore specifies the DataStore that should be used to store the Kubernetes data for the given Tenant Control Plane.
|
||||
When Kamaji runs with the default DataStore flag, all empty values will inherit the default value.
|
||||
By leaving it empty and running Kamaji with no default DataStore flag, it is possible to achieve automatic assignment to a specific DataStore object.
|
||||
|
||||
Migration from one DataStore to another backed by the same Driver is possible. See: https://kamaji.clastix.io/guides/datastore-migration/
|
||||
Migration from one DataStore to another backed by a different Driver is not supported.
|
||||
type: string
|
||||
dataStoreSchema:
|
||||
description: |-
|
||||
DataStoreSchema allows to specify the name of the database (for relational DataStores) or the key prefix (for etcd). This
|
||||
value is optional and immutable. Note that Kamaji currently doesn't ensure that DataStoreSchema values are unique. It's up
|
||||
to the user to avoid clashes between different TenantControlPlanes. If not set upon creation, Kamaji will default the
|
||||
DataStoreSchema by concatenating the namespace and name of the TenantControlPlane.
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- message: changing the dataStoreSchema is not supported
|
||||
rule: self == oldSelf
|
||||
kubernetes:
|
||||
description: Kubernetes specification for tenant control plane
|
||||
properties:
|
||||
@@ -6539,15 +6585,47 @@ spec:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
clusterDomain:
|
||||
default: cluster.local
|
||||
description: The default domain name used for DNS resolution within the cluster.
|
||||
pattern: .*\..*
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- message: changing the cluster domain is not supported
|
||||
rule: self == oldSelf
|
||||
dnsServiceIPs:
|
||||
default:
|
||||
- 10.96.0.10
|
||||
description: |-
|
||||
The DNS Service for internal resolution, it must match the Service CIDR.
|
||||
In case of an empty value, it is automatically computed according to the Service CIDR, e.g.:
|
||||
Service CIDR 10.96.0.0/16, the resulting DNS Service IP will be 10.96.0.10 for IPv4,
|
||||
for IPv6 from the CIDR 2001:db8:abcd::/64 the resulting DNS Service IP will be 2001:db8:abcd::10.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
loadBalancerClass:
|
||||
description: |-
|
||||
Specify the LoadBalancer class in case of multiple load balancer implementations.
|
||||
Field supported only for Tenant Control Plane instances exposed using a LoadBalancer Service.
|
||||
minLength: 1
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- message: LoadBalancerClass is immutable
|
||||
rule: self == oldSelf
|
||||
loadBalancerSourceRanges:
|
||||
description: |-
|
||||
LoadBalancerSourceRanges restricts the IP ranges that can access
|
||||
the LoadBalancer type Service. This field defines a list of IP
|
||||
address ranges (in CIDR format) that are allowed to access the service.
|
||||
If left empty, the service will allow traffic from all IP ranges (0.0.0.0/0).
|
||||
This feature is useful for restricting access to API servers or services
|
||||
to specific networks for security purposes.
|
||||
Example: {"192.168.1.0/24", "10.0.0.0/8"}
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
podCidr:
|
||||
default: 10.244.0.0/16
|
||||
description: CIDR for Kubernetes Pods
|
||||
description: 'CIDR for Kubernetes Pods: if empty, defaulted to 10.244.0.0/16.'
|
||||
type: string
|
||||
port:
|
||||
default: 6443
|
||||
@@ -6556,13 +6634,24 @@ spec:
|
||||
type: integer
|
||||
serviceCidr:
|
||||
default: 10.96.0.0/16
|
||||
description: Kubernetes Service
|
||||
description: 'CIDR for Kubernetes Services: if empty, defaulted to 10.96.0.0/16.'
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- controlPlane
|
||||
- kubernetes
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: unsetting the dataStore is not supported
|
||||
rule: '!has(oldSelf.dataStore) || has(self.dataStore)'
|
||||
- message: unsetting the dataStoreSchema is not supported
|
||||
rule: '!has(oldSelf.dataStoreSchema) || has(self.dataStoreSchema)'
|
||||
- message: LoadBalancer source ranges are supported only with LoadBalancer service type
|
||||
rule: '!has(self.networkProfile.loadBalancerSourceRanges) || (size(self.networkProfile.loadBalancerSourceRanges) == 0 || self.controlPlane.service.serviceType == ''LoadBalancer'')'
|
||||
- message: LoadBalancerClass is supported only with LoadBalancer service type
|
||||
rule: '!has(self.networkProfile.loadBalancerClass) || self.controlPlane.service.serviceType == ''LoadBalancer'''
|
||||
- message: LoadBalancerClass cannot be set or unset at runtime
|
||||
rule: self.controlPlane.service.serviceType != 'LoadBalancer' || (oldSelf.controlPlane.service.serviceType != 'LoadBalancer' && self.controlPlane.service.serviceType == 'LoadBalancer') || has(self.networkProfile.loadBalancerClass) == has(oldSelf.networkProfile.loadBalancerClass)
|
||||
status:
|
||||
description: TenantControlPlaneStatus defines the observed state of TenantControlPlane.
|
||||
properties:
|
||||
@@ -6747,6 +6836,7 @@ spec:
|
||||
Ports is a list of records of service ports
|
||||
If used, every port defined in the service should have an entry in it
|
||||
items:
|
||||
description: PortStatus represents the error condition of a service port
|
||||
properties:
|
||||
error:
|
||||
description: |-
|
||||
@@ -7228,6 +7318,7 @@ spec:
|
||||
Ports is a list of records of service ports
|
||||
If used, every port defined in the service should have an entry in it
|
||||
items:
|
||||
description: PortStatus represents the error condition of a service port
|
||||
properties:
|
||||
error:
|
||||
description: |-
|
||||
|
||||
@@ -33,8 +33,9 @@ spec:
|
||||
- --leader-elect
|
||||
- --metrics-bind-address={{ .Values.metricsBindAddress }}
|
||||
- --tmp-directory={{ .Values.temporaryDirectoryPath }}
|
||||
{{- $datastoreName := .Values.defaultDatastoreName | required ".Values.defaultDatastoreName is required!" }}
|
||||
- --datastore={{ $datastoreName }}
|
||||
{{- if not (eq .Values.defaultDatastoreName "") }}
|
||||
- --datastore={{ .Values.defaultDatastoreName }}
|
||||
{{- end }}
|
||||
{{- if .Values.telemetry.disabled }}
|
||||
- --disable-telemetry
|
||||
{{- end }}
|
||||
|
||||
@@ -95,7 +95,7 @@ loggingDevel:
|
||||
# -- Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error) (default false)
|
||||
enable: false
|
||||
|
||||
# -- Specify the default DataStore name for the Kamaji instance.
|
||||
# -- If specified, all the Kamaji instances with an unassigned DataStore will inherit this default value.
|
||||
defaultDatastoreName: default
|
||||
|
||||
kamaji-etcd:
|
||||
|
||||
@@ -41,21 +41,22 @@ import (
|
||||
func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
// CLI flags
|
||||
var (
|
||||
metricsBindAddress string
|
||||
healthProbeBindAddress string
|
||||
leaderElect bool
|
||||
tmpDirectory string
|
||||
kineImage string
|
||||
controllerReconcileTimeout time.Duration
|
||||
cacheResyncPeriod time.Duration
|
||||
datastore string
|
||||
managerNamespace string
|
||||
managerServiceAccountName string
|
||||
managerServiceName string
|
||||
webhookCABundle []byte
|
||||
migrateJobImage string
|
||||
maxConcurrentReconciles int
|
||||
disableTelemetry bool
|
||||
metricsBindAddress string
|
||||
healthProbeBindAddress string
|
||||
leaderElect bool
|
||||
tmpDirectory string
|
||||
kineImage string
|
||||
controllerReconcileTimeout time.Duration
|
||||
cacheResyncPeriod time.Duration
|
||||
datastore string
|
||||
managerNamespace string
|
||||
managerServiceAccountName string
|
||||
managerServiceName string
|
||||
webhookCABundle []byte
|
||||
migrateJobImage string
|
||||
maxConcurrentReconciles int
|
||||
disableTelemetry bool
|
||||
certificateExpirationDeadline time.Duration
|
||||
|
||||
webhookCAPath string
|
||||
)
|
||||
@@ -67,15 +68,19 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
Short: "Start the Kamaji Kubernetes Operator",
|
||||
SilenceErrors: false,
|
||||
SilenceUsage: true,
|
||||
PreRunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
PreRunE: func(cmd *cobra.Command, _ []string) (err error) {
|
||||
// Avoid to pollute Kamaji stdout with useless details by the underlying klog implementations
|
||||
klog.SetOutput(io.Discard)
|
||||
klog.LogToStderr(false)
|
||||
|
||||
if err = cmdutils.CheckFlags(cmd.Flags(), []string{"kine-image", "datastore", "migrate-image", "tmp-directory", "pod-namespace", "webhook-service-name", "serviceaccount-name", "webhook-ca-path"}...); err != nil {
|
||||
if err = cmdutils.CheckFlags(cmd.Flags(), []string{"kine-image", "migrate-image", "tmp-directory", "pod-namespace", "webhook-service-name", "serviceaccount-name", "webhook-ca-path"}...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if certificateExpirationDeadline < 24*time.Hour {
|
||||
return fmt.Errorf("certificate expiration deadline must be at least 24 hours")
|
||||
}
|
||||
|
||||
if webhookCABundle, err = os.ReadFile(webhookCAPath); err != nil {
|
||||
return fmt.Errorf("unable to read webhook CA: %w", err)
|
||||
}
|
||||
@@ -90,7 +95,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
|
||||
return nil
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(*cobra.Command, []string) error {
|
||||
setupLog := ctrl.Log.WithName("setup")
|
||||
|
||||
setupLog.Info(fmt.Sprintf("Kamaji version %s %s%s", internal.GitTag, internal.GitCommit, internal.GitDirty))
|
||||
@@ -186,7 +191,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
}
|
||||
}
|
||||
|
||||
if err = (&controllers.CertificateLifecycle{Channel: certChannel}).SetupWithManager(mgr); err != nil {
|
||||
if err = (&controllers.CertificateLifecycle{Channel: certChannel, Deadline: certificateExpirationDeadline}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "CertificateLifecycle")
|
||||
|
||||
return err
|
||||
@@ -214,6 +219,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
},
|
||||
},
|
||||
routes.TenantControlPlaneValidate{}: {
|
||||
handlers.TenantControlPlaneCertSANs{},
|
||||
handlers.TenantControlPlaneName{},
|
||||
handlers.TenantControlPlaneVersion{},
|
||||
handlers.TenantControlPlaneKubeletAddresses{},
|
||||
@@ -229,6 +235,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
},
|
||||
},
|
||||
handlers.TenantControlPlaneServiceCIDR{},
|
||||
handlers.TenantControlPlaneLoadBalancerSourceRanges{},
|
||||
},
|
||||
routes.TenantControlPlaneTelemetry{}: {
|
||||
handlers.TenantControlPlaneTelemetry{
|
||||
@@ -298,7 +305,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
cmd.Flags().BoolVar(&leaderElect, "leader-elect", true, "Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.")
|
||||
cmd.Flags().StringVar(&tmpDirectory, "tmp-directory", "/tmp/kamaji", "Directory which will be used to work with temporary files.")
|
||||
cmd.Flags().StringVar(&kineImage, "kine-image", "rancher/kine:v0.11.10-amd64", "Container image along with tag to use for the Kine sidecar container (used only if etcd-storage-type is set to one of kine strategies).")
|
||||
cmd.Flags().StringVar(&datastore, "datastore", "etcd", "The default DataStore that should be used by Kamaji to setup the required storage.")
|
||||
cmd.Flags().StringVar(&datastore, "datastore", "", "Optional, the default DataStore that should be used by Kamaji to setup the required storage of Tenant Control Planes with undeclared DataStore.")
|
||||
cmd.Flags().StringVar(&migrateJobImage, "migrate-image", fmt.Sprintf("clastix/kamaji:%s", internal.GitTag), "Specify the container image to launch when a TenantControlPlane is migrated to a new datastore.")
|
||||
cmd.Flags().IntVar(&maxConcurrentReconciles, "max-concurrent-tcp-reconciles", 1, "Specify the number of workers for the Tenant Control Plane controller (beware of CPU consumption)")
|
||||
cmd.Flags().StringVar(&managerNamespace, "pod-namespace", os.Getenv("POD_NAMESPACE"), "The Kubernetes Namespace on which the Operator is running in, required for the TenantControlPlane migration jobs.")
|
||||
@@ -308,6 +315,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
cmd.Flags().DurationVar(&controllerReconcileTimeout, "controller-reconcile-timeout", 30*time.Second, "The reconciliation request timeout before the controller withdraw the external resource calls, such as dealing with the Datastore, or the Tenant Control Plane API endpoint.")
|
||||
cmd.Flags().DurationVar(&cacheResyncPeriod, "cache-resync-period", 10*time.Hour, "The controller-runtime.Manager cache resync period.")
|
||||
cmd.Flags().BoolVar(&disableTelemetry, "disable-telemetry", false, "Disable the analytics traces collection.")
|
||||
cmd.Flags().DurationVar(&certificateExpirationDeadline, "certificate-expiration-deadline", 24*time.Hour, "Define the deadline upon certificate expiration to start the renewal process, cannot be less than a 24 hours.")
|
||||
|
||||
cobra.OnInitialize(func() {
|
||||
viper.AutomaticEnv()
|
||||
|
||||
@@ -31,7 +31,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
Use: "migrate",
|
||||
Short: "Migrate the data of a TenantControlPlane to another compatible DataStore",
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(*cobra.Command, []string) error {
|
||||
ctx, cancelFn := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancelFn()
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ func NewCmd(scheme *runtime.Scheme) *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "kamaji",
|
||||
Short: "Build and operate Kubernetes at scale with a fraction of operational burden.",
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
PersistentPreRun: func(*cobra.Command, []string) {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
utilruntime.Must(kamajiv1alpha1.AddToScheme(scheme))
|
||||
utilruntime.Must(appsv1.RegisterDefaults(scheme))
|
||||
|
||||
@@ -30,8 +30,10 @@ import (
|
||||
)
|
||||
|
||||
type CertificateLifecycle struct {
|
||||
Channel CertificateChannel
|
||||
client client.Client
|
||||
Channel CertificateChannel
|
||||
Deadline time.Duration
|
||||
|
||||
client client.Client
|
||||
}
|
||||
|
||||
func (s *CertificateLifecycle) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
|
||||
@@ -76,7 +78,7 @@ func (s *CertificateLifecycle) Reconcile(ctx context.Context, request reconcile.
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
deadline := time.Now().AddDate(0, 0, 1)
|
||||
deadline := time.Now().Add(s.Deadline)
|
||||
|
||||
if deadline.After(crt.NotAfter) {
|
||||
logger.Info("certificate near expiration, must be rotated")
|
||||
|
||||
@@ -101,7 +101,7 @@ func (r *DataStore) SetupWithManager(mgr controllerruntime.Manager) error {
|
||||
CreateFunc: func(_ context.Context, createEvent event.TypedCreateEvent[client.Object], w workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
enqueueFn(createEvent.Object.(*kamajiv1alpha1.TenantControlPlane), w)
|
||||
},
|
||||
UpdateFunc: func(ctx context.Context, updateEvent event.TypedUpdateEvent[client.Object], w workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
UpdateFunc: func(_ context.Context, updateEvent event.TypedUpdateEvent[client.Object], w workqueue.TypedRateLimitingInterface[reconcile.Request]) {
|
||||
enqueueFn(updateEvent.ObjectOld.(*kamajiv1alpha1.TenantControlPlane), w)
|
||||
enqueueFn(updateEvent.ObjectNew.(*kamajiv1alpha1.TenantControlPlane), w)
|
||||
},
|
||||
|
||||
@@ -182,7 +182,7 @@ func (m *Manager) Reconcile(ctx context.Context, request reconcile.Request) (res
|
||||
Metrics: metricsserver.Options{
|
||||
BindAddress: "0",
|
||||
},
|
||||
NewClient: func(config *rest.Config, options client.Options) (client.Client, error) {
|
||||
NewClient: func(config *rest.Config, _ client.Options) (client.Client, error) {
|
||||
return client.New(config, client.Options{
|
||||
Scheme: m.client.Scheme(),
|
||||
})
|
||||
|
||||
@@ -122,6 +122,12 @@ func (r *TenantControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R
|
||||
// Retrieving the DataStore to use for the current reconciliation
|
||||
ds, err := r.dataStore(ctx, tenantControlPlane)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrMissingDataStore) {
|
||||
log.Info(err.Error())
|
||||
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
log.Error(err, "cannot retrieve the DataStore for the given instance")
|
||||
|
||||
return ctrl.Result{}, err
|
||||
@@ -193,6 +199,12 @@ func (r *TenantControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R
|
||||
}
|
||||
|
||||
if err = utils.UpdateStatus(ctx, r.Client, tenantControlPlane, resource); err != nil {
|
||||
if kamajierrors.ShouldReconcileErrorBeIgnored(err) {
|
||||
log.V(1).Info("sentinel error, enqueuing back request", "error", err.Error())
|
||||
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
}
|
||||
|
||||
log.Error(err, "update of the resource failed", "resource", resource.GetName())
|
||||
|
||||
return ctrl.Result{}, err
|
||||
@@ -300,18 +312,23 @@ func (r *TenantControlPlaneReconciler) RemoveFinalizer(ctx context.Context, tena
|
||||
return r.Client.Update(ctx, tenantControlPlane)
|
||||
}
|
||||
|
||||
var ErrMissingDataStore = errors.New("the Tenant Control Plane doesn't have a DataStore assigned, and Kamaji is running with no default DataStore fallback")
|
||||
|
||||
// dataStore retrieves the override DataStore for the given Tenant Control Plane if specified,
|
||||
// otherwise fallback to the default one specified in the Kamaji setup.
|
||||
func (r *TenantControlPlaneReconciler) dataStore(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (*kamajiv1alpha1.DataStore, error) {
|
||||
dataStoreName := tenantControlPlane.Spec.DataStore
|
||||
if len(dataStoreName) == 0 {
|
||||
dataStoreName = r.Config.DefaultDataStoreName
|
||||
if tenantControlPlane.Spec.DataStore == "" && r.Config.DefaultDataStoreName == "" {
|
||||
return nil, ErrMissingDataStore
|
||||
}
|
||||
|
||||
ds := &kamajiv1alpha1.DataStore{}
|
||||
if err := r.Client.Get(ctx, k8stypes.NamespacedName{Name: dataStoreName}, ds); err != nil {
|
||||
if tenantControlPlane.Spec.DataStore == "" {
|
||||
tenantControlPlane.Spec.DataStore = r.Config.DefaultDataStoreName
|
||||
}
|
||||
|
||||
var ds kamajiv1alpha1.DataStore
|
||||
if err := r.Client.Get(ctx, k8stypes.NamespacedName{Name: tenantControlPlane.Spec.DataStore}, &ds); err != nil {
|
||||
return nil, errors.Wrap(err, "cannot retrieve *kamajiv1alpha.DataStore object")
|
||||
}
|
||||
|
||||
return ds, nil
|
||||
return &ds, nil
|
||||
}
|
||||
|
||||
@@ -104,7 +104,9 @@ spec:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
- apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
|
||||
33
deploy/kamaji-aws.env
Normal file
33
deploy/kamaji-aws.env
Normal file
@@ -0,0 +1,33 @@
|
||||
# aws parameters
|
||||
export KAMAJI_REGION=eu-west-3
|
||||
export KAMAJI_AZ=eu-west-3a
|
||||
export KAMAJI_CLUSTER_VERSION="1.32"
|
||||
export KAMAJI_CLUSTER=kamaji-2
|
||||
export KAMAJI_NODE_NG=${KAMAJI_CLUSTER}-${KAMAJI_REGION}-ng1
|
||||
export KAMAJI_NODE_TYPE=t3.medium
|
||||
export KAMAJI_VPC_NAME=eksctl-${KAMAJI_CLUSTER}-cluster/VPC
|
||||
export KAMAJI_VPC_CIDR=192.168.0.0/16
|
||||
export KAMAJI_PUBLIC_SUBNET_NAME=eksctl-${KAMAJI_CLUSTER}-cluster/SubnetPublicEUWEST3A
|
||||
export KAMAJI_PRIVATE_SUBNET_NAME=eksctl-${KAMAJI_CLUSTER}-cluster/SubnetPrivateEUWEST3A
|
||||
|
||||
|
||||
# kamaji parameters
|
||||
export KAMAJI_NAMESPACE=kamaji-system
|
||||
|
||||
# tenant cluster parameters
|
||||
export TENANT_NAMESPACE=tenant-00
|
||||
export TENANT_NAME=tenant-00
|
||||
export TENANT_DOMAIN=internal.kamaji.aws.com
|
||||
export TENANT_VERSION=v1.30.2
|
||||
export TENANT_PORT=6443 # port used to expose the tenant api server
|
||||
export TENANT_PROXY_PORT=8132 # port used to expose the konnectivity server
|
||||
export TENANT_POD_CIDR=10.36.0.0/16
|
||||
export TENANT_SVC_CIDR=10.96.0.0/16
|
||||
export TENANT_DNS_SERVICE=10.96.0.10
|
||||
|
||||
export TENANT_VM_SIZE=t3.medium
|
||||
export TENANT_ASG_MIN_SIZE=1
|
||||
export TENANT_ASG_MAX_SIZE=1
|
||||
export TENANT_ASG_DESIRED_SIZE=1
|
||||
export TENANT_SUBNET_ADDRESS=10.0.4.0/24
|
||||
export TENANT_ASG_NAME=$TENANT_NAME-workers
|
||||
20
docs/content/enterprise-addons/index.md
Normal file
20
docs/content/enterprise-addons/index.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# Enterprise Addons
|
||||
|
||||
This document contains the documentation of the available Kamaji addons.
|
||||
|
||||
## What is a Kamaji Addon
|
||||
|
||||
A Kamaji Addon is a separate component installed in the same Kamaji cluster.
|
||||
It offers an additional set of features required for enterprise-grade usage.
|
||||
|
||||
The developed Kamaji addons are closed-source available and work with the upstream Kamaji edition.
|
||||
|
||||
## Distribution of Addons
|
||||
|
||||
The Kamaji Addons are available behind an active [subscription license](https://clastix.io/support/).
|
||||
|
||||
Once a subscription is activated, the [CLASTIX](https://clastix.io) team will automate the push of required OCI (container images) and Helm Chart artefacts to the customer's OCI-compatible repository.
|
||||
|
||||
## Available addons
|
||||
|
||||
- [Ingress Addon](/enterprise-addons/ingress): expose Tenant Control Planes behind an Ingress Controller to reduce the amount of required LoadBalancer services
|
||||
182
docs/content/enterprise-addons/ingress.md
Normal file
182
docs/content/enterprise-addons/ingress.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# Ingress Addon
|
||||
|
||||
A Kubernetes API Server could be announced to users in several ways.
|
||||
The most preferred way is leveraging on Load Balancers with their dedicated IP.
|
||||
|
||||

|
||||

|
||||
|
||||
However, IPv4 addresses could be limited and scarce in availability, as well as expensive for public ones when running in the Cloud.
|
||||
A possible optimisation could be implementing an Ingress Controller which routes traffic to Kubernetes API Servers on a host-routing basis.
|
||||
|
||||
Despite this solution sounding optimal for end users, it brings some challenges from the worker nodes' standpoint.
|
||||
|
||||
## Challenges
|
||||
|
||||
Internally deployed applications that need to interact with the Kubernetes API Server will leverage on the `kubernetes` endpoint in the `default` namespace:
|
||||
every request sent to the `https://kubernetes.default.svc` endpoint will be forwarded to the Kubernetes API Server.
|
||||
|
||||
The routing put in place by the Kubernetes CNI is based on the L4, meaning that all the requests will be forwarded to the Ingress Controller with no `Host` header,
|
||||
making impossible a routing based on the FQDN.
|
||||
|
||||
## Solution
|
||||
|
||||
The `kamaji-addon-ingress` is an addon that will expose the Tenant Control Plane behind an Ingress Controller.
|
||||
It's responsible for creating an `Ingress` object with the required HTTP rules, as well as the annotations needed for the TLS/SSL passthrough.
|
||||
|
||||

|
||||

|
||||
|
||||
Following is the list of supported Ingress Controllers:
|
||||
|
||||
- [HAProxy Technologies Kubernetes Ingress](https://github.com/haproxytech/kubernetes-ingress)
|
||||
|
||||
> Active subscribers can request additional Ingress Controller flavours
|
||||
|
||||
## How to enable the Addon
|
||||
|
||||
Annotate the Tenant Control Plane instances with the key `kamaji.clastix.io/ingress.domain` and the domain suffix domain value:
|
||||
|
||||
```shell
|
||||
kubectl annotate tenantcontrolplane $NAME kamaji.clastix.io/ingress.domain=$SUFFIX_DOMAIN
|
||||
```
|
||||
|
||||
The value must be the expected suffix domain of generated resources.
|
||||
|
||||
```yaml
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: TenantControlPlane
|
||||
metadata:
|
||||
annotations:
|
||||
kamaji.clastix.io/ingress.domain: clastix.cloud # the expected kamaji-addon-ingress label
|
||||
name: tenant-00
|
||||
namespace: apezzuto
|
||||
```
|
||||
|
||||
Once a Tenant Control Plane has been annotated with this key, the addon will generate the following `Ingress` object.
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
haproxy.org/ssl-passthrough: "true"
|
||||
name: 592ee7b8-cd07-48cf-b754-76f370c3f87c
|
||||
namespace: apezzuto
|
||||
spec:
|
||||
ingressClassName: haproxy
|
||||
rules:
|
||||
- host: apezzuto-tenant-00.k8s.clastix.cloud
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: tenant-00
|
||||
port:
|
||||
number: 6443
|
||||
path: /
|
||||
pathType: Prefix
|
||||
- host: apezzuto-tenant-00.konnectivity.clastix.cloud
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: tenant-00
|
||||
port:
|
||||
number: 8132
|
||||
path: /
|
||||
pathType: Prefix
|
||||
```
|
||||
|
||||
The pattern for the generated hosts is the following:
|
||||
`${tcp.namespace}-${tcp.name}.{k8s|konnectivity}.${ADDON_ANNOTATION_VALUE}`
|
||||
|
||||
> Please, notice the `konnectivity` rule will be created only if the `konnectivity` addon has been enabled.
|
||||
|
||||
## Infrastructure requirements
|
||||
|
||||
For Tenant Control Plane objects leveraging on this addon, the following changes must be implemented.
|
||||
|
||||
### Ingress Controller
|
||||
|
||||
The Ingress Controller must be deployed to listen for `https` connection on the default port `443`:
|
||||
if you have different requirements, please, engage with the CLASTIX team.
|
||||
|
||||
### DNS resolution
|
||||
|
||||
The following zones must be configured properly according to your DNS provider:
|
||||
|
||||
```
|
||||
*.konnectivity.clastix.cloud A <YOUR_INGRESS_CONTROLLER_IP>
|
||||
*.k8s.clastix.cloud A <YOUR_INGRESS_CONTROLLER_IP>
|
||||
```
|
||||
|
||||
### Certificate SANs
|
||||
|
||||
```yaml
|
||||
networkProfile:
|
||||
certSANs:
|
||||
- apezzuto-tenant-00.k8s.clastix.cloud
|
||||
- apezzuto-tenant-00.konnectivity.clastix.cloud
|
||||
dnsServiceIPs:
|
||||
- 10.96.0.10
|
||||
podCidr: 10.244.0.0/16
|
||||
port: 6443
|
||||
serviceCidr: 10.96.0.0/16
|
||||
```
|
||||
|
||||
### Service type and Ingress
|
||||
|
||||
The Kubernetes API Server can be exposed using a `ClusterIP`, rather than a Load Balancer.
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
controlPlane:
|
||||
service:
|
||||
serviceType: ClusterIP
|
||||
ingress:
|
||||
hostname: apezzuto-tenant-00.k8s.clastix.cloud:443
|
||||
ingressClassName: unhandled
|
||||
```
|
||||
|
||||
The `ingressClassName` value must match a non-handled `IngressClass` object,
|
||||
the addon will take care of generating the correct object.
|
||||
|
||||
> Nota Bene: the `hostname` must absolutely point to the 443 port
|
||||
|
||||
### Kubernetes components extra Arguments
|
||||
|
||||
The Kubernetes API Server must start with the following flag:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
controlPlane:
|
||||
deployment:
|
||||
extraArgs:
|
||||
apiServer:
|
||||
- --endpoint-reconciler-type=none
|
||||
```
|
||||
|
||||
The `kamaji-addon-ingress` will be responsible for populating the `kubernetes` EndpointSlice object in the Tenant cluster.
|
||||
|
||||
If you're running with `konnectivity`, also this extra argument must be enforced:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
addons:
|
||||
konnectivity:
|
||||
agent:
|
||||
extraArgs:
|
||||
- --proxy-server-host=apezzuto-tenant-00.konnectivity.clastix.cloud
|
||||
- --proxy-server-port=443
|
||||
```
|
||||
|
||||
## Air-gapped environments
|
||||
|
||||
The `kamaji-addon-ingress` works with a deployed component in the Tenant Cluster based on the container image `docker.io/clastix/tcp-proxy:latest`.
|
||||
|
||||
The same image can be replaced by customising the Addon Helm value upon installation:
|
||||
|
||||
```
|
||||
--set options.tcpProxyImage=private.repository.tld/tcp-proxy:latest
|
||||
```
|
||||
6
docs/content/getting-started/index.md
Normal file
6
docs/content/getting-started/index.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# Getting started
|
||||
|
||||
This section contains the information on how to get started with Kamaji
|
||||
|
||||
- [Getting started with Kamaji](getting-started.md): install the required components and Kamaji on any Kubernetes cluster
|
||||
- [Kamaji: Getting started on Kind](kind.md): useful for development environments, create a Kamaji environment on `kind`
|
||||
143
docs/content/getting-started/kind.md
Normal file
143
docs/content/getting-started/kind.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Kamaji: Getting started on Kind
|
||||
This guide will lead you through the process of creating a setup of a working Kamaji setup using Kind clusters.
|
||||
|
||||
The guide requires the following installed:
|
||||
|
||||
- Docker
|
||||
- Kind
|
||||
- Helm
|
||||
|
||||
## Summary
|
||||
|
||||
* [Creating Kind Cluster](#creating-kind-cluster)
|
||||
* [Installing Dependencies: Cert-Manager](#installing-dependencies-cert-manager)
|
||||
* [Installing MetalLb](#installing-metallb)
|
||||
* [Creating IP Address Pool](#creating-ip-address-pool)
|
||||
* [Installing Kamaji](#installing-kamaji)
|
||||
|
||||
|
||||
## Creating Kind Cluster
|
||||
|
||||
Create a kind cluster.
|
||||
```
|
||||
kind create cluster --name kamaji
|
||||
```
|
||||
|
||||
This will take a short while for the kind cluster to created.
|
||||
|
||||
## Installing Dependencies: Cert-Manager
|
||||
|
||||
Kamaji has a dependency on Cert Manager, as it uses dynamic admission control, validating and mutating webhook configurations which are secured by a TLS communication, these certificates are managed by `cert-manager`. Hence, it needs to be added.
|
||||
|
||||
Add the Bitnami Repo to the Helm Manager.
|
||||
```
|
||||
helm repo add bitnami https://charts.bitnami.com/bitnami
|
||||
```
|
||||
|
||||
Install Cert Manager to the cluster using the bitnami charts using Helm --
|
||||
```
|
||||
helm upgrade --install cert-manager bitnami/cert-manager --namespace certmanager-system --create-namespace --set "installCRDs=true"
|
||||
```
|
||||
|
||||
This will install cert-manager to the cluster. You can watch the progress of the installation on the cluster using the command -
|
||||
```
|
||||
kubectl get pods -Aw
|
||||
```
|
||||
|
||||
!!! Info ""
|
||||
Another pre-requisite is to have a __storage provider__.
|
||||
|
||||
Kind by default provides `local-path-provisioner`, but one can have any other CSI Drivers. Since there are ETCD and Control-Planes running, having persistent volumes is essential for the cluster.
|
||||
|
||||
## Installing MetalLb
|
||||
|
||||
MetalLB is used in order to dynamically assign IP addresses to the components, and also define custom IP Address Pools.
|
||||
|
||||
Install MetalLb using the `kubectl` manifest apply command --
|
||||
```
|
||||
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.7/config/manifests/metallb-native.yaml
|
||||
```
|
||||
|
||||
This will install MetalLb onto the cluster with all the necessary resources.
|
||||
|
||||
## Creating IP Address Pool
|
||||
|
||||
Extract the Gateway IP of the network Kind is running on.
|
||||
```
|
||||
GW_IP=$(docker network inspect -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}' kind)
|
||||
```
|
||||
|
||||
Modify the IP Address, and create the resource to be added to the cluster to create the IP Address Pool.
|
||||
```
|
||||
NET_IP=$(echo ${GW_IP} | sed -E 's|^([0-9]+\.[0-9]+)\..*$|\1|g')
|
||||
cat << EOF | sed -E "s|172.19|${NET_IP}|g" | kubectl apply -f -
|
||||
apiVersion: metallb.io/v1beta1
|
||||
kind: IPAddressPool
|
||||
metadata:
|
||||
name: kind-ip-pool
|
||||
namespace: metallb-system
|
||||
spec:
|
||||
addresses:
|
||||
- 172.19.255.200-172.19.255.250
|
||||
---
|
||||
apiVersion: metallb.io/v1beta1
|
||||
kind: L2Advertisement
|
||||
metadata:
|
||||
name: emtpy
|
||||
namespace: metallb-system
|
||||
EOF
|
||||
```
|
||||
|
||||
## Installing Kamaji
|
||||
|
||||
- Add the Clastix Repo in the Helm Repo lists.
|
||||
```
|
||||
helm repo add clastix https://clastix.github.io/charts
|
||||
helm repo update
|
||||
```
|
||||
|
||||
- Install Kamaji
|
||||
```
|
||||
helm upgrade --install kamaji clastix/kamaji --namespace kamaji-system --create-namespace --set 'resources=null'
|
||||
```
|
||||
|
||||
- Watch the progress of the deployments --
|
||||
```
|
||||
kubectl get pods -Aw
|
||||
```
|
||||
|
||||
- Verify by first checking Kamaji CRDs.
|
||||
```
|
||||
kubectl get crds | grep -i kamaji
|
||||
```
|
||||
|
||||
- Install a Tenant Control Plane using the command --
|
||||
|
||||
```
|
||||
kubectl apply -f https://raw.githubusercontent.com/clastix/kamaji/master/config/samples/kamaji_v1alpha1_tenantcontrolplane.yaml
|
||||
```
|
||||
|
||||
- Watch the progress of the Tenant Control Plane by ---
|
||||
```
|
||||
kubectl get tcp -w
|
||||
```
|
||||
|
||||
- You can attempt to get the details of the control plane by downloading the kubeconfig file ---
|
||||
```
|
||||
# Set the SECRET as KUBECONFIG column listed in the tcp output.
|
||||
SECRET=""
|
||||
kubectl get secret $SECRET -o jsonpath='{.data.admin\.conf}'|base64 -d > /tmp/kamaji.conf
|
||||
```
|
||||
|
||||
- Export the KUBECONFIG
|
||||
```
|
||||
export KUBECONFIG=/tmp/kamaji.conf
|
||||
```
|
||||
|
||||
- Notice that the `kubectl` version changes, and there is no nodes now.
|
||||
```
|
||||
kubectl version
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
A Video Tutorial of the [demonstration](https://www.youtube.com/watch?v=hDTvnOyUmo4&t=577s) can also be viewed.
|
||||
@@ -94,7 +94,10 @@ k8s-126-576c775b5d-jmvlm 4/4 Running 0 50s
|
||||
The Kamaji operator will run a controller which processes all the Secrets to determine their expiration, both for the `kubeconfig`, as well as for the certificates.
|
||||
|
||||
The controller, named `CertificateLifecycle`, will extract the certificates from the _Secret_ objects notifying the `TenantControlPlaneReconciler` controller which will start a new certificate rotation.
|
||||
The rotation will occur the day before their expiration.
|
||||
By default, the rotation will occur the day before their expiration.
|
||||
|
||||
This rotation deadline can be dynamically configured using the Kamaji CLI flag `--certificate-expiration-deadline` using the Go _Duration_ syntax:
|
||||
e.g.: set the value `7d` to trigger the renewal a week before the effective expiration date.
|
||||
|
||||
> Nota Bene:
|
||||
>
|
||||
|
||||
426
docs/content/guides/kamaji-aws-deployment.md
Normal file
426
docs/content/guides/kamaji-aws-deployment.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# Setup Kamaji on AWS
|
||||
|
||||
This guide will lead you through the process of creating a working Kamaji setup on on AWS.
|
||||
|
||||
The guide requires:
|
||||
|
||||
- a bootstrap machine
|
||||
- a Kubernetes cluster (EKS) to run the Admin and Tenant Control Planes
|
||||
- an arbitrary number of machines to host `Tenant`s' workloads
|
||||
|
||||
## Summary
|
||||
|
||||
* [Prepare the bootstrap workspace](#prepare-the-bootstrap-workspace)
|
||||
* [Access Management Cluster](#access-management-cluster)
|
||||
* [Install Kamaji](#install-kamaji)
|
||||
* [Create Tenant Cluster](#create-tenant-cluster)
|
||||
* [Cleanup](#cleanup)
|
||||
|
||||
## Prepare the bootstrap workspace
|
||||
|
||||
On the bootstrap machine, clone the repo and prepare the workspace directory:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/clastix/kamaji
|
||||
cd kamaji/deploy
|
||||
```
|
||||
|
||||
We assume you have installed on the bootstrap machine:
|
||||
|
||||
- [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl)
|
||||
- [helm](https://helm.sh/docs/intro/install/)
|
||||
- [jq](https://stedolan.github.io/jq/)
|
||||
- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)
|
||||
- [eksctl](https://eksctl.io/installation/)
|
||||
- [clusterawsadm](https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases)
|
||||
|
||||
Make sure you have a valid AWS Account, and login to AWS:
|
||||
|
||||
> The easiest way to get started with AWS is to create [access keys](https://docs.aws.amazon.com/cli/v1/userguide/cli-authentication-user.html#cli-authentication-user-configure.title) associated to your account
|
||||
|
||||
```bash
|
||||
aws configure
|
||||
```
|
||||
|
||||
## Create Management cluster
|
||||
|
||||
In Kamaji, a Management Cluster is a regular Kubernetes cluster which hosts zero to many Tenant Cluster Control Planes. The Management Cluster acts as a cockpit for all the Tenant clusters and implements monitoring, logging, and governance of all the Kamaji setups, including all Tenant Clusters. For this guide, we're going to use an instance of AWS Kubernetes Service (EKS) as a Management Cluster.
|
||||
|
||||
Throughout the following instructions, shell variables are used to indicate values that you should adjust to your own AWS environment:
|
||||
|
||||
### Create EKS cluster
|
||||
|
||||
In order to create quickly an EKS cluster, we will use `eksctl` provided by AWS. `eksctl` is a simple CLI tool for creating and managing clusters on EKS
|
||||
|
||||
`eksctl` will provision for you:
|
||||
|
||||
- A dedicated VPC on `192.168.0.0/16` CIDR
|
||||
- 3 private subnets and 3 public subnets in 3 different availability zones
|
||||
- NAT Gateway for the private subnets, An internet gateway for the public ones
|
||||
- The required route tables to associate the subnets with the IGW and the NAT gateways
|
||||
- Provision the EKS cluster
|
||||
- Provision worker nodes and associate them to your cluster
|
||||
- Optionally creates the required IAM policies for your addons and attach them to the node
|
||||
- Optionally, install the EKS add-ons to your cluster
|
||||
|
||||
For our use case, we will create an EKS cluster with the following configuration:
|
||||
|
||||
```bash
|
||||
cat >eks-cluster.yaml <<EOF
|
||||
apiVersion: eksctl.io/v1alpha5
|
||||
kind: ClusterConfig
|
||||
|
||||
metadata:
|
||||
name: ${KAMAJI_CLUSTER}
|
||||
region: ${KAMAJI_REGION}
|
||||
version: ${KAMAJI_CLUSTER_VERSION}
|
||||
iam:
|
||||
withOIDC: true
|
||||
vpc:
|
||||
clusterEndpoints:
|
||||
privateAccess: true
|
||||
publicAccess: true
|
||||
managedNodeGroups:
|
||||
- name: ${KAMAJI_NODE_NG}
|
||||
labels: { role: workers }
|
||||
instanceType: ${KAMAJI_NODE_TYPE}
|
||||
desiredCapacity: 1
|
||||
privateNetworking: true
|
||||
availabilityZones: [${KAMAJI_AZ}]
|
||||
iam:
|
||||
withAddonPolicies:
|
||||
certManager: true
|
||||
ebs: true
|
||||
externalDNS: true
|
||||
addons:
|
||||
- name: aws-ebs-csi-driver
|
||||
EOF
|
||||
|
||||
eks create cluster -f eks-cluster.yaml
|
||||
|
||||
```
|
||||
|
||||
Please note :
|
||||
|
||||
- The `aws-ebs-csi-driver` addon is required to use EBS volumes as persistent volumes. This will be mainly used to store the tenant control plane data using the _default_ `etcd` DataStore.
|
||||
- We created a node group with 1 node in one availability zone to simplify the setup.
|
||||
|
||||
### Access to the management cluster
|
||||
|
||||
And check you can access:
|
||||
|
||||
```bash
|
||||
aws eks update-kubeconfig --region ${KAMAJI_REGION} --name ${KAMAJI_CLUSTER}
|
||||
kubectl cluster-info
|
||||
# make ebs as a default storage class
|
||||
kubectl patch storageclass gp2 -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'
|
||||
|
||||
```
|
||||
|
||||
### (optional) Add route 53 domain
|
||||
|
||||
In order to easily access tenant clusters, it is recommended to create a Route53 domain or use an existing one if it exists
|
||||
|
||||
```bash
|
||||
# for within VPC
|
||||
aws route53 create-hosted-zone --name "$TENANT_DOMAIN" --caller-reference $(date +%s) --vpc "VPCRegion=$KAMAJI_REGION,VPCId=$KAMAJI_VPC_ID"
|
||||
|
||||
```
|
||||
|
||||
## Install Kamaji
|
||||
|
||||
Follow the [Getting Started](../getting-started.md) to install Cert Manager and the Kamaji Controller.
|
||||
|
||||
### Install Cert Manager
|
||||
|
||||
Kamaji takes advantage of the [dynamic admission control](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/), such as validating and mutating webhook configurations. These webhooks are secured by a TLS communication, and the certificates are managed by [`cert-manager`](https://cert-manager.io/), making it a prerequisite that must be installed:
|
||||
|
||||
```bash
|
||||
helm repo add jetstack https://charts.jetstack.io
|
||||
helm repo update
|
||||
helm install \
|
||||
cert-manager jetstack/cert-manager \
|
||||
--namespace cert-manager \
|
||||
--create-namespace \
|
||||
--version v1.11.0 \
|
||||
--set installCRDs=true
|
||||
```
|
||||
|
||||
### (optional) Install ExternalDNS
|
||||
|
||||
ExternalDNS allows updating your DNS records dynamically from an annotation that you add in the service within EKS. Run the following commands to install the ExternalDNS Helm chart:
|
||||
|
||||
```bash
|
||||
|
||||
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
|
||||
helm repo update
|
||||
helm install external-dns external-dns/external-dns \
|
||||
--namespace external-dns \
|
||||
--create-namespace \
|
||||
--version 1.15.1
|
||||
```
|
||||
|
||||
## Install Kamaji Controller
|
||||
|
||||
Installing Kamaji via Helm charts is the preferred way. Run the following commands to install a stable release of Kamaji:
|
||||
|
||||
```bash
|
||||
helm repo add clastix https://clastix.github.io/charts
|
||||
helm repo update
|
||||
helm install kamaji clastix/kamaji -n kamaji-system --create-namespace
|
||||
```
|
||||
|
||||
## Create a Tenant Cluster
|
||||
|
||||
Now that our management cluster is up and running, we can create a Tenant Cluster. A Tenant Cluster is a Kubernetes cluster that is managed by Kamaji.
|
||||
|
||||
### Tenant Control Plane
|
||||
|
||||
A tenant cluster is made of a `Tenant Control Plane` and an arbitrary number of worker nodes. The `Tenant Control Plane` is a Kubernetes Control Plane managed by Kamaji and responsible for running the Tenant's workloads.
|
||||
|
||||
Before creating a Tenant Control Plane, you need to define some variables:
|
||||
|
||||
```bash
|
||||
export KAMAJI_VPC_ID=$(aws ec2 describe-vpcs --filters "Name=tag:Name,Values=$KAMAJI_VPC_NAME" --query "Vpcs[0].VpcId" --output text)
|
||||
export KAMAJI_PUBLIC_SUBNET_ID=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$KAMAJI_VPC_ID" --filters "Name=tag:Name,Values=$KAMAJI_PUBLIC_SUBNET_NAME" --query "Subnets[0].SubnetId" --output text)
|
||||
|
||||
export TENANT_EIP_ID=$(aws ec2 allocate-address --query 'AllocationId' --output text)
|
||||
export TENANT_PUBLIC_IP=$(aws ec2 describe-addresses --allocation-ids $TENANT_EIP_ID --query 'Addresses[0].PublicIp' --output text)
|
||||
|
||||
|
||||
```
|
||||
|
||||
In the next step, we will create a Tenant Control Plane with the following configuration:
|
||||
|
||||
```yaml
|
||||
cat > ${TENANT_NAMESPACE}-${TENANT_NAME}.yaml <<EOF
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: ${TENANT_NAMESPACE}
|
||||
---
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: TenantControlPlane
|
||||
metadata:
|
||||
name: ${TENANT_NAME}
|
||||
namespace: ${TENANT_NAMESPACE}
|
||||
labels:
|
||||
tenant.clastix.io: ${TENANT_NAME}
|
||||
spec:
|
||||
dataStore: default
|
||||
controlPlane:
|
||||
deployment:
|
||||
replicas: 1
|
||||
nodeSelector:
|
||||
topology.kubernetes.io/zone: ${KAMAJI_AZ}
|
||||
additionalMetadata:
|
||||
labels:
|
||||
tenant.clastix.io: ${TENANT_NAME}
|
||||
extraArgs:
|
||||
apiServer: []
|
||||
controllerManager: []
|
||||
scheduler: []
|
||||
resources:
|
||||
apiServer:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits: {}
|
||||
controllerManager:
|
||||
requests:
|
||||
cpu: 125m
|
||||
memory: 256Mi
|
||||
limits: {}
|
||||
scheduler:
|
||||
requests:
|
||||
cpu: 125m
|
||||
memory: 256Mi
|
||||
limits: {}
|
||||
service:
|
||||
additionalMetadata:
|
||||
labels:
|
||||
tenant.clastix.io: ${TENANT_NAME}
|
||||
annotations:
|
||||
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: tcp
|
||||
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
|
||||
service.beta.kubernetes.io/aws-load-balancer-subnets: ${KAMAJI_PUBLIC_SUBNET_ID}
|
||||
service.beta.kubernetes.io/aws-load-balancer-eip-allocations: ${TENANT_EIP_ID}
|
||||
service.beta.kubernetes.io/aws-load-balancer-type: nlb
|
||||
external-dns.alpha.kubernetes.io/hostname: ${TENANT_NAME}.${TENANT_DOMAIN}
|
||||
serviceType: LoadBalancer
|
||||
kubernetes:
|
||||
version: ${TENANT_VERSION}
|
||||
kubelet:
|
||||
cgroupfs: systemd
|
||||
admissionControllers:
|
||||
- ResourceQuota
|
||||
- LimitRanger
|
||||
networkProfile:
|
||||
address: ${TENANT_PUBLIC_IP}
|
||||
port: ${TENANT_PORT}
|
||||
certSANs:
|
||||
- ${TENANT_NAME}.${TENANT_DOMAIN}
|
||||
serviceCidr: ${TENANT_SVC_CIDR}
|
||||
podCidr: ${TENANT_POD_CIDR}
|
||||
dnsServiceIPs:
|
||||
- ${TENANT_DNS_SERVICE}
|
||||
addons:
|
||||
coreDNS: {}
|
||||
kubeProxy: {}
|
||||
konnectivity:
|
||||
server:
|
||||
port: ${TENANT_PROXY_PORT}
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
limits: {}
|
||||
EOF
|
||||
|
||||
kubectl -n ${TENANT_NAMESPACE} apply -f ${TENANT_NAMESPACE}-${TENANT_NAME}.yaml
|
||||
```
|
||||
|
||||
Make sure:
|
||||
|
||||
- Tenant Control Plane will expose the API server using a public IP address through a network load balancer.
|
||||
it is important to provide a static public IP address for the API server in order to make it reachable from the outside world.
|
||||
|
||||
- The following annotation: `external-dns.alpha.kubernetes.io/hostname` is set to create the DNS record. It tells AWS to expose the Tenant Control Plane with a public domain name: `${TENANT_NAME}.${TENANT_DOMAIN}`.
|
||||
|
||||
> Since AWS load Balancer does not support setting LoadBalancerIP, you will get the following warning on the service created for the control plane tenant `Error syncing load balancer: failed to ensure load balancer: LoadBalancerIP cannot be specified for AWS ELB`. you can ignore it for now.
|
||||
|
||||
### Working with Tenant Control Plane
|
||||
|
||||
Check the access to the Tenant Control Plane:
|
||||
|
||||
> If the domain you used is a private route53 domain make sure to map the public IP of the LB to `${TENANT_NAME}.${TENANT_DOMAIN}` in your `/etc/hosts`. otherwise, `kubectl` will fail to check SSL certificates
|
||||
|
||||
```bash
|
||||
curl -k https://${TENANT_PUBLIC_IP}:${TENANT_PORT}/version
|
||||
curl -k https://${TENANT_NAME}.${TENANT_DOMAIN}:${TENANT_PORT}/healthz
|
||||
curl -k https://${TENANT_NAME}.${TENANT_DOMAIN}:${TENANT_PORT}/version
|
||||
```
|
||||
|
||||
Let's retrieve the `kubeconfig` in order to work with it:
|
||||
|
||||
```bash
|
||||
kubectl get secrets -n ${TENANT_NAMESPACE} ${TENANT_NAME}-admin-kubeconfig -o json \
|
||||
| jq -r '.data["admin.conf"]' \
|
||||
| base64 --decode \
|
||||
> ${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig
|
||||
|
||||
kubectl --kubeconfig=${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig config \
|
||||
set-cluster ${TENANT_NAME} \
|
||||
--server https://${TENANT_NAME}.${TENANT_DOMAIN}:${TENANT_PORT}
|
||||
```
|
||||
|
||||
and let's check it out:
|
||||
|
||||
```bash
|
||||
kubectl --kubeconfig=${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig get svc
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 38h
|
||||
```
|
||||
|
||||
Check out how the Tenant Control Plane advertises itself:
|
||||
|
||||
```bash
|
||||
kubectl --kubeconfig=${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig get ep
|
||||
|
||||
NAME ENDPOINTS AGE
|
||||
kubernetes 13.37.33.12:6443 3m22s
|
||||
```
|
||||
|
||||
## Join worker nodes
|
||||
|
||||
The Tenant Control Plane is made of pods running in the Kamaji Management Cluster. At this point, the Tenant Cluster has no worker nodes. So, the next step is to join some worker nodes to the Tenant Control Plane.
|
||||
|
||||
Kamaji does not provide any helper for the creation of tenant worker nodes, instead, it leverages the [Cluster Management API](https://github.com/kubernetes-sigs/cluster-api). This allows you to create the Tenant Clusters, including worker nodes, in a completely declarative way. Currently, a Cluster API `ControlPlane` provider for AWS is available: check the [official documentation](https://github.com/clastix/cluster-api-control-plane-provider-kamaji/blob/master/docs/providers-aws.md).
|
||||
|
||||
An alternative approach to create and join worker nodes in AWS is to manually create the VMs, turn them into Kubernetes worker nodes and then join through the `kubeadm` command.
|
||||
|
||||
### Generate kubeadm join command
|
||||
|
||||
To join the worker nodes to the Tenant Control Plane, you need to generate the `kubeadm join` command from the Management cluster:
|
||||
|
||||
```bash
|
||||
TENANT_ADDR=$(kubectl -n ${TENANT_NAMESPACE} get svc ${TENANT_NAME} -o json | jq -r ."spec.loadBalancerIP")
|
||||
JOIN_CMD=$(echo "sudo kubeadm join ${TENANT_ADDR}:6443 ")$(kubeadm --kubeconfig=${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig token create --ttl 0 --print-join-command |cut -d" " -f4-)
|
||||
```
|
||||
|
||||
> Setting `--ttl=0` on the `kubeadm token create` will guarantee that the token will never expire and can be used every time.
|
||||
>
|
||||
> It's not intended for production-grade setups.
|
||||
|
||||
### Create tenant worker nodes
|
||||
|
||||
In this section, we will use AMI provided by CAPA (Cluster API Provider AWS) to create the worker nodes. Those AMIs are built using [image builder](https://github.com/kubernetes-sigs/image-builder/tree/main) and contain all the necessary components to join the cluster.
|
||||
|
||||
```bash
|
||||
|
||||
export KAMAJI_PRIVATE_SUBNET_ID=$(aws ec2 describe-subnets --filters "Name=vpc-id,Values=$KAMAJI_VPC_ID" --filters "Name=tag:Name,Values=$KAMAJI_PRIVATE_SUBNET_NAME" --query "Subnets[0].SubnetId" --output text)
|
||||
|
||||
export WORKER_AMI=$(clusterawsadm ami list --kubernetes-version=$TENANT_VERSION --os=ubuntu-24.04 --region=$KAMAJI_REGION -o json | jq -r .items[0].spec.imageID)
|
||||
|
||||
cat <<EOF >> worker-user-data.sh
|
||||
#!/bin/bash
|
||||
|
||||
$JOIN_CMD
|
||||
EOF
|
||||
|
||||
aws ec2 run-instances --image-id $WORKER_AMI --instance-type "t2.medium" --user-data $(cat worker-user-data.sh | base64 -w0) --network-interfaces '{"SubnetId":'"'${KAMAJI_PRIVATE_SUBNET_ID}'"',"AssociatePublicIpAddress":false,"DeviceIndex":0,"Groups":["<REPLACE_WITH_SG>"]}' --count "1"
|
||||
|
||||
```
|
||||
|
||||
> We have used user data to run the `kubeadm join` command on the instance boot. This will make sure that the worker node will join the cluster automatically.
|
||||
|
||||
|
||||
> Make sure to replace `<REPLACE_WITH_SG>` with the security group id that allows the worker nodes to communicate with the public IP of the tenant control plane
|
||||
|
||||
Checking the nodes in the Tenant Cluster:
|
||||
|
||||
```bash
|
||||
kubectl --kubeconfig=${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig get nodes
|
||||
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
ip-192-168-153-94 NotReady <none> 56m v1.30.2
|
||||
```
|
||||
|
||||
The cluster needs a [CNI](https://kubernetes.io/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/) plugin to get the nodes ready. In this guide, we are going to install [calico](https://projectcalico.docs.tigera.io/about/about-calico), but feel free to use one of your taste.
|
||||
|
||||
Download the latest stable Calico manifest:
|
||||
|
||||
```bash
|
||||
curl https://raw.githubusercontent.com/projectcalico/calico/v3.24.1/manifests/calico.yaml -O
|
||||
```
|
||||
|
||||
As per [documentation](https://projectcalico.docs.tigera.io/reference/public-cloud/AWS), Calico in VXLAN mode is supported on AWS while IPIP packets are blocked by the AWS network fabric. Make sure you edit the manifest above and set the following variables:
|
||||
|
||||
- `CLUSTER_TYPE="k8s"`
|
||||
- `CALICO_IPV4POOL_IPIP="Never"`
|
||||
- `CALICO_IPV4POOL_VXLAN="Always"`
|
||||
|
||||
Apply to the Tenant Cluster:
|
||||
|
||||
```bash
|
||||
kubectl --kubeconfig=${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig apply -f calico.yaml
|
||||
```
|
||||
|
||||
And after a while, nodes will be ready
|
||||
|
||||
```bash
|
||||
kubectl --kubeconfig=${TENANT_NAMESPACE}-${TENANT_NAME}.kubeconfig get nodes
|
||||
|
||||
NAME STATUS ROLES AGE VERSION
|
||||
ip-192-168-153-94 Ready <none> 59m v1.30.2
|
||||
```
|
||||
|
||||
## Cleanup
|
||||
|
||||
To get rid of the whole Kamaji infrastructure, remove the EKS cluster:
|
||||
|
||||
```bash
|
||||
eksctl delete cluster -f eks-cluster.yaml
|
||||
|
||||
That's all folks!
|
||||
BIN
docs/content/images/kamaji-addon-ingress-ic-dark.png
Normal file
BIN
docs/content/images/kamaji-addon-ingress-ic-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
BIN
docs/content/images/kamaji-addon-ingress-ic.png
Normal file
BIN
docs/content/images/kamaji-addon-ingress-ic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 89 KiB |
BIN
docs/content/images/kamaji-addon-ingress-lb-dark.png
Normal file
BIN
docs/content/images/kamaji-addon-ingress-lb-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
BIN
docs/content/images/kamaji-addon-ingress-lb.png
Normal file
BIN
docs/content/images/kamaji-addon-ingress-lb.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -14,7 +14,7 @@ View [Concepts](concepts.md) for a deeper understanding of principles behind Kam
|
||||
|
||||
## Getting started
|
||||
|
||||
Please refer to the [Getting Started guide](getting-started.md) to deploy a minimal setup of Kamaji.
|
||||
Please refer to the [Getting Started guide](getting-started/index.md) to deploy a minimal setup of Kamaji.
|
||||
|
||||
|
||||
## FAQs
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,16 +10,19 @@ Edge Release artifacts are published on a monthly basis as part of the open sour
|
||||
|
||||
Edge Release artifacts contain the code in from the main branch at the point in time when they were cut. This means they always have the latest features and fixes, and have undergone automated testing as well as maintainer code review. Edge Releases may involve partial features that are later modified or backed out. They may also involve breaking changes, of course, we do our best to avoid this. Edge Releases are generally considered production ready, and the project will mark specific releases as “_not recommended_” if bugs are discovered after release.
|
||||
|
||||
| Kamaji | Management Cluster | Tenant Cluster |
|
||||
|--------------|--------------------|----------------------|
|
||||
| edge-24.12.1 | v1.22+ | [v1.29.0 .. v1.31.4] |
|
||||
|
||||
|
||||
Using Edge Release artifacts and reporting bugs helps us ensure a rapid pace of development and is a great way to help maintainers. We publish edge release guidance as part of the release notes and strive to always provide production-ready artifacts.
|
||||
|
||||
### Stable Releases
|
||||
|
||||
Stable Release artifacts of Kamaji follow semantic versioning, whereby changes in major version denote large feature additions and possible breaking changes and changes in minor versions denote safe upgrades without breaking changes.
|
||||
Stable Release artifacts of Kamaji follow semantic versioning, whereby changes in major version denote large feature additions and possible breaking changes and changes in minor versions denote safe upgrades without breaking changes. As of July 2024 [Clastix Labs](https://github.com/clastix) organization does no longer provide stable release artifacts. Latest stable release available is:
|
||||
|
||||
| Kamaji | Management Cluster | Tenant Cluster |
|
||||
|--------|--------------------|----------------------|
|
||||
| v1.0.0 | v1.22+ | [v1.21.0 .. v1.30.2] |
|
||||
|
||||
As of July 2024 [Clastix Labs](https://github.com/clastix) organization does no longer provide stable release artifacts. Stable Release artifacts are offered on a subscription basis by [CLASTIX](https://clastix.io), the main Kamaji project contributor.
|
||||
|
||||
> Learn more about [available subscription plans](https://clastix.io/support/) from CLASTIX.
|
||||
Stable Release artifacts are offered now on a subscription basis by [CLASTIX](https://clastix.io), the main Kamaji project contributor. Learn more about [available subscription plans](https://clastix.io/support/) provided by CLASTIX.
|
||||
|
||||
@@ -55,11 +55,15 @@ markdown_extensions:
|
||||
# Generate navigation bar
|
||||
nav:
|
||||
- 'Kamaji': index.md
|
||||
- 'Getting started': getting-started.md
|
||||
- 'Getting started':
|
||||
- getting-started/index.md
|
||||
- getting-started/getting-started.md
|
||||
- getting-started/kind.md
|
||||
- 'Concepts': concepts.md
|
||||
- 'Guides':
|
||||
- guides/index.md
|
||||
- guides/kamaji-azure-deployment.md
|
||||
- guides/kamaji-aws-deployment.md
|
||||
- guides/alternative-datastore.md
|
||||
- guides/kamaji-gitops-flux.md
|
||||
- guides/upgrade.md
|
||||
@@ -77,4 +81,7 @@ nav:
|
||||
- reference/versioning.md
|
||||
- reference/api.md
|
||||
- 'Telemetry': telemetry.md
|
||||
- 'Enterprise Addons':
|
||||
- enterprise-addons/index.md
|
||||
- enterprise-addons/ingress.md
|
||||
- 'Contribute': contribute.md
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
//nolint:ginkgolinter
|
||||
package e2e
|
||||
|
||||
import (
|
||||
|
||||
220
go.mod
220
go.mod
@@ -1,50 +1,49 @@
|
||||
module github.com/clastix/kamaji
|
||||
|
||||
go 1.22.0
|
||||
|
||||
toolchain go1.22.1
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/JamesStewy/go-mysqldump v0.2.2
|
||||
github.com/blang/semver v3.5.1+incompatible
|
||||
github.com/clastix/kamaji-telemetry v1.0.0
|
||||
github.com/docker/docker v27.3.0+incompatible
|
||||
github.com/docker/docker v27.5.1+incompatible
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-pg/pg/v10 v10.13.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/go-pg/pg/v10 v10.14.0
|
||||
github.com/go-sql-driver/mysql v1.9.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/juju/mutex/v2 v2.0.0
|
||||
github.com/nats-io/nats.go v1.37.0
|
||||
github.com/onsi/ginkgo/v2 v2.20.2
|
||||
github.com/onsi/gomega v1.34.2
|
||||
github.com/nats-io/nats.go v1.39.1
|
||||
github.com/onsi/ginkgo/v2 v2.23.2
|
||||
github.com/onsi/gomega v1.36.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/testcontainers/testcontainers-go v0.33.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.16
|
||||
go.etcd.io/etcd/client/v3 v3.5.16
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0
|
||||
k8s.io/api v0.31.1
|
||||
k8s.io/apimachinery v0.31.1
|
||||
k8s.io/apiserver v0.31.1
|
||||
k8s.io/client-go v0.31.1
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/spf13/viper v1.20.0
|
||||
github.com/testcontainers/testcontainers-go v0.35.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.19
|
||||
go.etcd.io/etcd/client/v3 v3.5.19
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0
|
||||
k8s.io/api v0.32.3
|
||||
k8s.io/apimachinery v0.32.3
|
||||
k8s.io/apiserver v0.32.3
|
||||
k8s.io/client-go v0.32.3
|
||||
k8s.io/cluster-bootstrap v0.0.0
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kubelet v0.0.0
|
||||
k8s.io/kubernetes v1.31.1
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
|
||||
sigs.k8s.io/controller-runtime v0.19.0
|
||||
k8s.io/kubernetes v1.32.3
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
|
||||
sigs.k8s.io/controller-runtime v0.20.3
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.18.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/NYTimes/gziphandler v1.1.1 // indirect
|
||||
@@ -57,55 +56,52 @@ require (
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/coredns/caddy v1.1.1 // indirect
|
||||
github.com/coredns/corefile-migration v1.0.23 // indirect
|
||||
github.com/coredns/corefile-migration v1.0.24 // indirect
|
||||
github.com/coreos/go-semver v0.3.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.4 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-pg/zerochecker v0.2.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/cel-go v0.20.1 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/cel-go v0.22.0 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.6 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/juju/errors v0.0.0-20220203013757-bd733f3c86b9 // indirect
|
||||
github.com/klauspost/compress v1.17.4 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/lithammer/dedent v1.1.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
@@ -117,11 +113,11 @@ require (
|
||||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||
github.com/nats-io/nkeys v0.4.9 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
@@ -129,15 +125,15 @@ require (
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/stoewer/go-strcase v1.2.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
@@ -149,91 +145,89 @@ require (
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.19 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.26.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.35.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.24.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
|
||||
google.golang.org/grpc v1.65.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
golang.org/x/net v0.36.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
golang.org/x/tools v0.30.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||
google.golang.org/grpc v1.67.3 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.31.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.32.3 // indirect
|
||||
k8s.io/cli-runtime v0.0.0 // indirect
|
||||
k8s.io/cloud-provider v0.0.0 // indirect
|
||||
k8s.io/component-base v0.31.1 // indirect
|
||||
k8s.io/component-helpers v0.31.1 // indirect
|
||||
k8s.io/controller-manager v0.31.1 // indirect
|
||||
k8s.io/cri-api v0.31.1 // indirect
|
||||
k8s.io/component-base v0.32.3 // indirect
|
||||
k8s.io/component-helpers v0.32.3 // indirect
|
||||
k8s.io/controller-manager v0.32.3 // indirect
|
||||
k8s.io/cri-api v0.32.3 // indirect
|
||||
k8s.io/cri-client v0.0.0 // indirect
|
||||
k8s.io/kms v0.31.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
|
||||
k8s.io/kms v0.32.3 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
k8s.io/kube-proxy v0.0.0 // indirect
|
||||
k8s.io/system-validators v1.8.0 // indirect
|
||||
k8s.io/system-validators v1.9.1 // indirect
|
||||
mellium.im/sasl v0.3.1 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/kustomize/api v0.17.2 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.18.0 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
k8s.io/api => k8s.io/api v0.31.1
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.1
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.31.1
|
||||
k8s.io/apiserver => k8s.io/apiserver v0.31.1
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.31.1
|
||||
k8s.io/client-go => k8s.io/client-go v0.31.1
|
||||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.31.1
|
||||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.31.1
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.31.1
|
||||
k8s.io/component-base => k8s.io/component-base v0.31.1
|
||||
k8s.io/component-helpers => k8s.io/component-helpers v0.31.1
|
||||
k8s.io/controller-manager => k8s.io/controller-manager v0.31.1
|
||||
k8s.io/cri-api => k8s.io/cri-api v0.31.1
|
||||
k8s.io/cri-client => k8s.io/cri-client v0.31.1
|
||||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.31.1
|
||||
k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.31.1
|
||||
k8s.io/endpointslice => k8s.io/endpointslice v0.31.1
|
||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.31.1
|
||||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.31.1
|
||||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.31.1
|
||||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.31.1
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.31.1
|
||||
k8s.io/kubelet => k8s.io/kubelet v0.31.1
|
||||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.31.1
|
||||
k8s.io/metrics => k8s.io/metrics v0.31.1
|
||||
k8s.io/mount-utils => k8s.io/mount-utils v0.31.1
|
||||
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.31.1
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.31.1
|
||||
k8s.io/api => k8s.io/api v0.32.3
|
||||
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.32.3
|
||||
k8s.io/apimachinery => k8s.io/apimachinery v0.32.3
|
||||
k8s.io/apiserver => k8s.io/apiserver v0.32.3
|
||||
k8s.io/cli-runtime => k8s.io/cli-runtime v0.32.3
|
||||
k8s.io/client-go => k8s.io/client-go v0.32.3
|
||||
k8s.io/cloud-provider => k8s.io/cloud-provider v0.32.3
|
||||
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.32.3
|
||||
k8s.io/code-generator => k8s.io/code-generator v0.32.3
|
||||
k8s.io/component-base => k8s.io/component-base v0.32.3
|
||||
k8s.io/component-helpers => k8s.io/component-helpers v0.32.3
|
||||
k8s.io/controller-manager => k8s.io/controller-manager v0.32.3
|
||||
k8s.io/cri-api => k8s.io/cri-api v0.32.3
|
||||
k8s.io/cri-client => k8s.io/cri-client v0.32.3
|
||||
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.32.3
|
||||
k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.32.3
|
||||
k8s.io/endpointslice => k8s.io/endpointslice v0.32.3
|
||||
k8s.io/externaljwt => k8s.io/externaljwt v0.32.3
|
||||
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.32.3
|
||||
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.32.3
|
||||
k8s.io/kube-proxy => k8s.io/kube-proxy v0.32.3
|
||||
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.32.3
|
||||
k8s.io/kubectl => k8s.io/kubectl v0.32.3
|
||||
k8s.io/kubelet => k8s.io/kubelet v0.32.3
|
||||
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.32.3
|
||||
k8s.io/metrics => k8s.io/metrics v0.32.3
|
||||
k8s.io/mount-utils => k8s.io/mount-utils v0.32.3
|
||||
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.32.3
|
||||
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.32.3
|
||||
)
|
||||
|
||||
replace github.com/JamesStewy/go-mysqldump => github.com/vtoma/go-mysqldump v1.0.0
|
||||
|
||||
441
go.sum
441
go.sum
@@ -1,13 +1,13 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
|
||||
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
@@ -26,31 +26,25 @@ github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/clastix/kamaji-telemetry v1.0.0 h1:/s7TVsyQpunD+cBKIaWmZ1yCXXYXgf4uQ4TeXio4moY=
|
||||
github.com/clastix/kamaji-telemetry v1.0.0/go.mod h1:yhK/I0qEmKQw4mtEZRUnQfjsbbrIZtuR/XXISBrrETU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/coredns/caddy v1.1.1 h1:2eYKZT7i6yxIfGP3qLJoJ7HAsDJqYB+X68g4NYjSrE0=
|
||||
github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4=
|
||||
github.com/coredns/corefile-migration v1.0.23 h1:Fp4FETmk8sT/IRgnKX2xstC2dL7+QdcU+BL5AYIN3Jw=
|
||||
github.com/coredns/corefile-migration v1.0.23/go.mod h1:8HyMhuyzx9RLZp8cRc9Uf3ECpEAafHOFxQWUPqktMQI=
|
||||
github.com/coredns/corefile-migration v1.0.24 h1:NL/zRKijhJZLYlNnMr891DRv5jXgfd3Noons1M6oTpc=
|
||||
github.com/coredns/corefile-migration v1.0.24/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY=
|
||||
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
|
||||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E=
|
||||
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
|
||||
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
@@ -60,8 +54,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/docker v27.3.0+incompatible h1:BNb1QY6o4JdKpqwi9IB+HUYcRRrVN4aGFUTvDmWYK1A=
|
||||
github.com/docker/docker v27.3.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8=
|
||||
github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -70,19 +64,17 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
|
||||
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
@@ -96,63 +88,49 @@ github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
|
||||
github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU=
|
||||
github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-pg/pg/v10 v10.13.0 h1:xMagDE57VP8Y2KvIf9PvrsOAIjX62XqaKmfEzB0c5eU=
|
||||
github.com/go-pg/pg/v10 v10.13.0/go.mod h1:IXp9Ok9JNNW9yWedbQxxvKUv84XhoH5+tGd+68y+zDs=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-pg/pg/v10 v10.14.0 h1:giXuPsJaWjzwzFJTxy39eBgGE44jpqH1jwv0uI3kBUU=
|
||||
github.com/go-pg/pg/v10 v10.14.0/go.mod h1:6kizZh54FveJxw9XZdNg07x7DDBWNsQrSiJS04MLwO8=
|
||||
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
|
||||
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
|
||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo=
|
||||
github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
|
||||
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
|
||||
github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84=
|
||||
github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
|
||||
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
|
||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -160,8 +138,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
|
||||
@@ -170,16 +148,12 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
@@ -207,8 +181,8 @@ github.com/juju/version/v2 v2.0.0-20211007103408-2e8da085dc23/go.mod h1:Ljlbryh9
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
@@ -227,8 +201,6 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
@@ -252,29 +224,28 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
||||
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
github.com/nats-io/nats.go v1.39.1 h1:oTkfKBmz7W047vRxV762M67ZdXeOtUgvbBaNoQ+3PPk=
|
||||
github.com/nats-io/nats.go v1.39.1/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM=
|
||||
github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=
|
||||
github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v1.14.2 h1:8mVmC9kjFFmA8H4pKMUhcblgifdkOIXPvbhN1T36q1M=
|
||||
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||
github.com/onsi/ginkgo/v2 v2.23.2 h1:LYLd7Wz401p0N7xR8y7WL6D2QZwKpbirDg0EVIvzvMM=
|
||||
github.com/onsi/ginkgo/v2 v2.23.2/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
|
||||
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -286,7 +257,6 @@ github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
@@ -296,10 +266,8 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
|
||||
@@ -314,19 +282,18 @@ github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
|
||||
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY=
|
||||
github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
@@ -340,12 +307,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
|
||||
github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
|
||||
github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo=
|
||||
github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4=
|
||||
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
|
||||
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
@@ -366,8 +333,8 @@ github.com/vtoma/go-mysqldump v1.0.0 h1:TNQXlsGD4r1+P9cMyzeWnOgHy0QbP0R+XfXqu5Is
|
||||
github.com/vtoma/go-mysqldump v1.0.0/go.mod h1:i9PUM5mb4MH+4D4tJktCTLWDy6CX5rDCL/nS4+f3N5w=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk=
|
||||
github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
|
||||
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -375,159 +342,122 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
|
||||
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
||||
go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E=
|
||||
go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8=
|
||||
go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg=
|
||||
go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE=
|
||||
go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw=
|
||||
go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok=
|
||||
go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
go.etcd.io/etcd/api/v3 v3.5.19 h1:w3L6sQZGsWPuBxRQ4m6pPP3bVUtV8rjW033EGwlr0jw=
|
||||
go.etcd.io/etcd/api/v3 v3.5.19/go.mod h1:QqKGViq4KTgOG43dr/uH0vmGWIaoJY3ggFi6ZH0TH/U=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.19 h1:9VsyGhg0WQGjDWWlDI4VuaS9PZJGNbPkaHEIuLwtixk=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.19/go.mod h1:qaOi1k4ZA9lVLejXNvyPABrVEe7VymMF2433yyRQ7O0=
|
||||
go.etcd.io/etcd/client/v2 v2.305.16 h1:kQrn9o5czVNaukf2A2At43cE9ZtWauOtf9vRZuiKXow=
|
||||
go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE=
|
||||
go.etcd.io/etcd/client/v3 v3.5.19 h1:+4byIz6ti3QC28W0zB0cEZWwhpVHXdrKovyycJh1KNo=
|
||||
go.etcd.io/etcd/client/v3 v3.5.19/go.mod h1:FNzyinmMIl0oVsty1zA3hFeUrxXI/JpEnz4sG+POzjU=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.16 h1:cnavs5WSPWeK4TYwPYfmcr3Joz9BH+TZ6qoUtz6/+mc=
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16 h1:zBXA3ZUpYs1AwiLGPafYAKKl/CORn/uaxYDwlNwndAk=
|
||||
go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI=
|
||||
go.etcd.io/etcd/server/v3 v3.5.16 h1:d0/SAdJ3vVsZvF8IFVb1k8zqMZ+heGcNfft71ul9GWE=
|
||||
go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
|
||||
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
|
||||
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
|
||||
go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo=
|
||||
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
|
||||
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
|
||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY=
|
||||
go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
|
||||
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
|
||||
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
|
||||
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
||||
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
|
||||
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||
google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
|
||||
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
@@ -536,8 +466,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
@@ -545,7 +473,6 @@ gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -553,63 +480,61 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
|
||||
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
|
||||
k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40=
|
||||
k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ=
|
||||
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
|
||||
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c=
|
||||
k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM=
|
||||
k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
|
||||
k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
|
||||
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
|
||||
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
|
||||
k8s.io/cloud-provider v0.31.1 h1:40b6AgDizwm5eWratZbqubTHMob25VWr6NX2Ei5TwZA=
|
||||
k8s.io/cloud-provider v0.31.1/go.mod h1:xAdkE7fdZdu9rKLuOZUMBfagu7bM+bas3iPux/2nLGg=
|
||||
k8s.io/cluster-bootstrap v0.31.1 h1:lS5aJi2r6WEKnjO5UhbYsz8e3xmEfoF4Hiob/gnB/Nk=
|
||||
k8s.io/cluster-bootstrap v0.31.1/go.mod h1:dxroRr4eQ0ekxis/kzGa1qODprQXAxQZrgDLfTk8Pug=
|
||||
k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8=
|
||||
k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w=
|
||||
k8s.io/component-helpers v0.31.1 h1:5hZUf3747atdgtR3gPntrG35rC2CkK7rYq2KUraz6Os=
|
||||
k8s.io/component-helpers v0.31.1/go.mod h1:ye0Gi8KzFNTfpIuzvVDtxJQMP/0Owkukf1vGf22Hl6U=
|
||||
k8s.io/controller-manager v0.31.1 h1:bwiy8y//EG5lJL2mdbOvZWrOgw2EXXIvwp95VYgoIis=
|
||||
k8s.io/controller-manager v0.31.1/go.mod h1:O440MSE6EI1AEVhB2Fc8FYqv6r8BHrSXjm5aj3886No=
|
||||
k8s.io/cri-api v0.31.1 h1:x0aI8yTI7Ho4c8tpuig8NwI/MRe+VhjiYyyebC2xphQ=
|
||||
k8s.io/cri-api v0.31.1/go.mod h1:Po3TMAYH/+KrZabi7QiwQI4a692oZcUOUThd/rqwxrI=
|
||||
k8s.io/cri-client v0.31.1 h1:w5D7BAhiaSVVDZqHs7YUZPpuUCybx8tCxfdBuDBw7zo=
|
||||
k8s.io/cri-client v0.31.1/go.mod h1:voVfZexZQwvlf/JD8w30sGN0k22LRcHRfCj7+m4kAXE=
|
||||
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
|
||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||
k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY=
|
||||
k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
|
||||
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
|
||||
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8=
|
||||
k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc=
|
||||
k8s.io/cli-runtime v0.32.3 h1:khLF2ivU2T6Q77H97atx3REY9tXiA3OLOjWJxUrdvss=
|
||||
k8s.io/cli-runtime v0.32.3/go.mod h1:vZT6dZq7mZAca53rwUfdFSZjdtLyfF61mkf/8q+Xjak=
|
||||
k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU=
|
||||
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||
k8s.io/cloud-provider v0.32.3 h1:WC7KhWrqXsU4b0E4tjS+nBectGiJbr1wuc1TpWXvtZM=
|
||||
k8s.io/cloud-provider v0.32.3/go.mod h1:/fwBfgRPuh16n8vLHT+PPT+Bc4LAEaJYj38opO2wsYY=
|
||||
k8s.io/cluster-bootstrap v0.32.3 h1:AqIpsUhB6MUeaAsl1WvaUw54AHRd2hfZrESlKChtd8s=
|
||||
k8s.io/cluster-bootstrap v0.32.3/go.mod h1:CHbBwgOb6liDV6JFUTkx5t85T2xidy0sChBDoyYw344=
|
||||
k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k=
|
||||
k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI=
|
||||
k8s.io/component-helpers v0.32.3 h1:9veHpOGTPLluqU4hAu5IPOwkOIZiGAJUhHndfVc5FT4=
|
||||
k8s.io/component-helpers v0.32.3/go.mod h1:utTBXk8lhkJewBKNuNf32Xl3KT/0VV19DmiXU/SV4Ao=
|
||||
k8s.io/controller-manager v0.32.3 h1:jBxZnQ24k6IMeWLyxWZmpa3QVS7ww+osAIzaUY/jqyc=
|
||||
k8s.io/controller-manager v0.32.3/go.mod h1:out1L3DZjE/p7JG0MoMMIaQGWIkt3c+pKaswqSHgKsI=
|
||||
k8s.io/cri-api v0.32.3 h1:E8VXbXNn4yAgmuKTeNzg0C1MFSxzTdlHSwUvjuYlPTY=
|
||||
k8s.io/cri-api v0.32.3/go.mod h1:DCzMuTh2padoinefWME0G678Mc3QFbLMF2vEweGzBAI=
|
||||
k8s.io/cri-client v0.32.3 h1:+D2ajlFpXsUcr/9ofYcE5kVqVK4Q97wnZHeH80oDEzw=
|
||||
k8s.io/cri-client v0.32.3/go.mod h1:W1+Z8QsVnLkoGqtJ41B5SRHfQn6/mqGORdfNDl2cEkw=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kms v0.31.1 h1:cGLyV3cIwb0ovpP/jtyIe2mEuQ/MkbhmeBF2IYCA9Io=
|
||||
k8s.io/kms v0.31.1/go.mod h1:OZKwl1fan3n3N5FFxnW5C4V3ygrah/3YXeJWS3O6+94=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
|
||||
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
|
||||
k8s.io/kube-proxy v0.31.1 h1:jODw/T0LipsysugosF8JUqGNPiae5DLj3DHrK13iYDk=
|
||||
k8s.io/kube-proxy v0.31.1/go.mod h1:u5CXxT6M89GuyPbSTYGzsfuQj1yDoSkqAOvITFfhNXw=
|
||||
k8s.io/kubelet v0.31.1 h1:aAxwVxGzbbMKKk/FnSjvkN52K3LdHhjhzmYcyGBuE0c=
|
||||
k8s.io/kubelet v0.31.1/go.mod h1:8ZbexYHqUO946gXEfFmnMZiK2UKRGhk7LlGvJ71p2Ig=
|
||||
k8s.io/kubernetes v1.31.1 h1:1fcYJe8SAhtannpChbmnzHLwAV9Je99PrGaFtBvCxms=
|
||||
k8s.io/kubernetes v1.31.1/go.mod h1:/YGPL//Fb9mdv5vukvAQ7Xon+Bqwry52bmjTdORAw+Q=
|
||||
k8s.io/system-validators v1.8.0 h1:tq05tdO9zdJZnNF3SXrq6LE7Knc/KfJm5wk68467JDg=
|
||||
k8s.io/system-validators v1.8.0/go.mod h1:gP1Ky+R9wtrSiFbrpEPwWMeYz9yqyy1S/KOh0Vci7WI=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
|
||||
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/kms v0.32.3 h1:HhHw5+pRCzEJp3oFFJ1q5W2N6gAI7YkUg4ay4Z0dgwM=
|
||||
k8s.io/kms v0.32.3/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/kube-proxy v0.32.3 h1:ibblchTx2SN3K7fTRPmAF0vYaTJqfkKFP3HV/0VDQMA=
|
||||
k8s.io/kube-proxy v0.32.3/go.mod h1:q7AjOWJqJQNSK29sdO3V686+zO9xbbVlPSPXCvjafTk=
|
||||
k8s.io/kubelet v0.32.3 h1:B9HzW4yB67flx8tN2FYuDwZvxnmK3v5EjxxFvOYjmc8=
|
||||
k8s.io/kubelet v0.32.3/go.mod h1:yyAQSCKC+tjSlaFw4HQG7Jein+vo+GeKBGdXdQGvL1U=
|
||||
k8s.io/kubernetes v1.32.3 h1:2A58BlNME8NwsMawmnM6InYo3Jf35Nw5G79q46kXwoA=
|
||||
k8s.io/kubernetes v1.32.3/go.mod h1:GvhiBeolvSRzBpFlgM0z/Bbu3Oxs9w3P6XfEgYaMi8k=
|
||||
k8s.io/system-validators v1.9.1 h1:O8xrr08foamG+1uQjAdiTLt/fT+QQJ4QNREfCWvuOws=
|
||||
k8s.io/system-validators v1.9.1/go.mod h1:d4UVrxKu52s0BHU984Peb9VpIq4V9sd8xjTBV/waY/I=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
mellium.im/sasl v0.3.1 h1:wE0LW6g7U83vhvxjC1IY8DnXM+EU095yeo8XClvCdfo=
|
||||
mellium.im/sasl v0.3.1/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
|
||||
sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g=
|
||||
sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0=
|
||||
sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ=
|
||||
sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/controller-runtime v0.20.3 h1:I6Ln8JfQjHH7JbtCD2HCYHoIzajoRxPNuvhvcDbZgkI=
|
||||
sigs.k8s.io/controller-runtime v0.20.3/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=
|
||||
sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
|
||||
@@ -31,11 +31,13 @@ import (
|
||||
const (
|
||||
kubernetesPKIVolumeName = "etc-kubernetes-pki"
|
||||
caCertificatesVolumeName = "etc-ca-certificates"
|
||||
sslCertsVolumeName = "etc-ssl-certs"
|
||||
usrShareCACertificatesVolumeName = "usr-share-ca-certificates"
|
||||
usrLocalShareCaCertificateVolumeName = "usr-local-share-ca-certificates"
|
||||
schedulerKubeconfigVolumeName = "scheduler-kubeconfig"
|
||||
controllerManagerKubeconfigVolumeName = "controller-manager-kubeconfig"
|
||||
kineUDSVolume = "kine-uds"
|
||||
kineUDSFolder = "/uds"
|
||||
kineUDSPath = kineUDSFolder + "/kine"
|
||||
dataStoreCertsVolumeName = "kine-config"
|
||||
kineVolumeCertName = "kine-certs"
|
||||
)
|
||||
@@ -159,7 +161,6 @@ func (d Deployment) setVolumes(podSpec *corev1.PodSpec, tcp kamajiv1alpha1.Tenan
|
||||
for _, fn := range []func(*corev1.PodSpec, kamajiv1alpha1.TenantControlPlane){
|
||||
d.buildPKIVolume,
|
||||
d.buildCAVolume,
|
||||
d.buildSSLCertsVolume,
|
||||
d.buildShareCAVolume,
|
||||
d.buildLocalShareCAVolume,
|
||||
d.buildSchedulerVolume,
|
||||
@@ -247,22 +248,6 @@ func (d Deployment) buildCAVolume(podSpec *corev1.PodSpec, tcp kamajiv1alpha1.Te
|
||||
}
|
||||
}
|
||||
|
||||
func (d Deployment) buildSSLCertsVolume(podSpec *corev1.PodSpec, tcp kamajiv1alpha1.TenantControlPlane) {
|
||||
found, index := utilities.HasNamedVolume(podSpec.Volumes, sslCertsVolumeName)
|
||||
if !found {
|
||||
index = len(podSpec.Volumes)
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
}
|
||||
|
||||
podSpec.Volumes[index].Name = sslCertsVolumeName
|
||||
podSpec.Volumes[index].VolumeSource = corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: tcp.Status.Certificates.CA.SecretName,
|
||||
DefaultMode: pointer.To(int32(420)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (d Deployment) buildShareCAVolume(podSpec *corev1.PodSpec, tcp kamajiv1alpha1.TenantControlPlane) {
|
||||
found, index := utilities.HasNamedVolume(podSpec.Volumes, usrShareCACertificatesVolumeName)
|
||||
if !found {
|
||||
@@ -518,11 +503,6 @@ func (d Deployment) buildControllerManager(podSpec *corev1.PodSpec, tenantContro
|
||||
ReadOnly: true,
|
||||
MountPath: "/etc/ca-certificates",
|
||||
})
|
||||
d.ensureVolumeMount(&volumeMounts, corev1.VolumeMount{
|
||||
Name: sslCertsVolumeName,
|
||||
ReadOnly: true,
|
||||
MountPath: "/etc/ssl/certs",
|
||||
})
|
||||
d.ensureVolumeMount(&volumeMounts, corev1.VolumeMount{
|
||||
Name: usrShareCACertificatesVolumeName,
|
||||
ReadOnly: true,
|
||||
@@ -632,6 +612,16 @@ func (d Deployment) buildKubeAPIServer(podSpec *corev1.PodSpec, tenantControlPla
|
||||
|
||||
volumeMounts := d.initVolumeMounts(kubernetesPKIVolumeName, podSpec.Containers[index].VolumeMounts, extraVolumeMounts...)
|
||||
|
||||
if d.DataStore.Spec.Driver == kamajiv1alpha1.KineMySQLDriver ||
|
||||
d.DataStore.Spec.Driver == kamajiv1alpha1.KinePostgreSQLDriver ||
|
||||
d.DataStore.Spec.Driver == kamajiv1alpha1.KineNatsDriver {
|
||||
d.ensureVolumeMount(&volumeMounts, corev1.VolumeMount{
|
||||
Name: kineUDSVolume,
|
||||
ReadOnly: false,
|
||||
MountPath: kineUDSFolder,
|
||||
})
|
||||
}
|
||||
|
||||
d.ensureVolumeMount(&volumeMounts, corev1.VolumeMount{
|
||||
Name: kubernetesPKIVolumeName,
|
||||
ReadOnly: true,
|
||||
@@ -642,11 +632,6 @@ func (d Deployment) buildKubeAPIServer(podSpec *corev1.PodSpec, tenantControlPla
|
||||
ReadOnly: true,
|
||||
MountPath: "/etc/ca-certificates",
|
||||
})
|
||||
d.ensureVolumeMount(&volumeMounts, corev1.VolumeMount{
|
||||
Name: sslCertsVolumeName,
|
||||
ReadOnly: true,
|
||||
MountPath: "/etc/ssl/certs",
|
||||
})
|
||||
d.ensureVolumeMount(&volumeMounts, corev1.VolumeMount{
|
||||
Name: usrShareCACertificatesVolumeName,
|
||||
ReadOnly: true,
|
||||
@@ -711,7 +696,7 @@ func (d Deployment) buildKubeAPIServerCommand(tenantControlPlane kamajiv1alpha1.
|
||||
|
||||
switch d.DataStore.Spec.Driver {
|
||||
case kamajiv1alpha1.KineMySQLDriver, kamajiv1alpha1.KinePostgreSQLDriver, kamajiv1alpha1.KineNatsDriver:
|
||||
desiredArgs["--etcd-servers"] = "http://127.0.0.1:2379"
|
||||
desiredArgs["--etcd-servers"] = "unix://" + kineUDSPath
|
||||
case kamajiv1alpha1.EtcdDriver:
|
||||
httpsEndpoints := make([]string, 0, len(d.DataStore.Spec.Endpoints))
|
||||
|
||||
@@ -751,7 +736,7 @@ func (d Deployment) secretProjection(secretName, certKeyName, keyName string) *c
|
||||
}
|
||||
|
||||
func (d Deployment) removeKineVolumes(podSpec *corev1.PodSpec) {
|
||||
for _, volumeName := range []string{kineVolumeCertName, dataStoreCertsVolumeName} {
|
||||
for _, volumeName := range []string{kineVolumeCertName, dataStoreCertsVolumeName, kineUDSVolume} {
|
||||
if found, index := utilities.HasNamedVolume(podSpec.Volumes, volumeName); found {
|
||||
var volumes []corev1.Volume
|
||||
|
||||
@@ -768,7 +753,20 @@ func (d Deployment) buildKineVolume(podSpec *corev1.PodSpec, tcp kamajiv1alpha1.
|
||||
return
|
||||
}
|
||||
|
||||
found, index := utilities.HasNamedVolume(podSpec.Volumes, dataStoreCertsVolumeName)
|
||||
found, index := utilities.HasNamedVolume(podSpec.Volumes, kineUDSVolume)
|
||||
if !found {
|
||||
index = len(podSpec.Volumes)
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
}
|
||||
|
||||
podSpec.Volumes[index].Name = kineUDSVolume
|
||||
podSpec.Volumes[index].VolumeSource = corev1.VolumeSource{
|
||||
EmptyDir: &corev1.EmptyDirVolumeSource{
|
||||
Medium: "Memory",
|
||||
},
|
||||
}
|
||||
|
||||
found, index = utilities.HasNamedVolume(podSpec.Volumes, dataStoreCertsVolumeName)
|
||||
if !found {
|
||||
index = len(podSpec.Volumes)
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
@@ -830,6 +828,8 @@ func (d Deployment) buildKine(podSpec *corev1.PodSpec, tcp kamajiv1alpha1.Tenant
|
||||
// Building kine arguments, taking in consideration the user-space ones if provided.
|
||||
args := map[string]string{}
|
||||
|
||||
args["--listen-address"] = "unix://" + kineUDSPath
|
||||
|
||||
if d.DataStore.Spec.TLSConfig != nil {
|
||||
// Ensuring the init container required for kine is present:
|
||||
// a chmod is required for kine in order to read the certificates to connect to the secured datastore.
|
||||
@@ -908,6 +908,11 @@ func (d Deployment) buildKine(podSpec *corev1.PodSpec, tcp kamajiv1alpha1.Tenant
|
||||
MountPath: "/certs",
|
||||
ReadOnly: false,
|
||||
},
|
||||
{
|
||||
Name: kineUDSVolume,
|
||||
MountPath: kineUDSFolder,
|
||||
ReadOnly: false,
|
||||
},
|
||||
}
|
||||
podSpec.Containers[index].Env = []corev1.EnvVar{
|
||||
{
|
||||
@@ -984,7 +989,7 @@ func (d Deployment) templateLabels(ctx context.Context, tenantControlPlane *kama
|
||||
"component.kamaji.clastix.io/front-proxy-client-certificate": hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.Certificates.FrontProxyClient.SecretName),
|
||||
"component.kamaji.clastix.io/service-account": hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.Certificates.SA.SecretName),
|
||||
"component.kamaji.clastix.io/scheduler-kubeconfig": hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.KubeConfig.Scheduler.SecretName),
|
||||
"component.kamaji.clastix.io/datastore": tenantControlPlane.Spec.DataStore,
|
||||
"component.kamaji.clastix.io/datastore": tenantControlPlane.Status.Storage.DataStoreName,
|
||||
}
|
||||
|
||||
return labels
|
||||
|
||||
@@ -5,6 +5,7 @@ package crypto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
@@ -13,9 +14,11 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
mathrand "math/rand"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// CheckPublicAndPrivateKeyValidity checks if the given bytes for the private and public keys are valid.
|
||||
@@ -34,7 +37,40 @@ func CheckPublicAndPrivateKeyValidity(publicKey []byte, privateKey []byte) (bool
|
||||
return false, err
|
||||
}
|
||||
|
||||
return checkPublicKeys(privKey.PublicKey, *pubKey), nil
|
||||
return checkPublicKeys(pubKey, privKey), nil
|
||||
}
|
||||
|
||||
// CheckCertificateNamesAndIPs checks if the Kubernetes API Server certificate matches the Control Plane Endpoint and SAN stored in the kubeadm:
|
||||
// it must check both IPs and DNS names, and returns a false if the required entry isn't available.
|
||||
// In case of removal of entries, this function returns true nevertheless to avoid reloading a Control Plane uselessly.
|
||||
func CheckCertificateNamesAndIPs(certificateBytes []byte, entries []string) (bool, error) {
|
||||
crt, err := ParseCertificateBytes(certificateBytes)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ips := sets.New[string]()
|
||||
for _, ip := range crt.IPAddresses {
|
||||
ips.Insert(ip.String())
|
||||
}
|
||||
|
||||
dns := sets.New[string](crt.DNSNames...)
|
||||
|
||||
for _, e := range entries {
|
||||
if ip := net.ParseIP(e); ip != nil {
|
||||
if !ips.Has(ip.String()) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !dns.Has(e) {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// CheckCertificateAndPrivateKeyPairValidity checks if the certificate and private key pair are valid.
|
||||
@@ -79,12 +115,21 @@ func ParseCertificateBytes(content []byte) (*x509.Certificate, error) {
|
||||
}
|
||||
|
||||
// ParsePrivateKeyBytes takes the private key bytes returning an RSA private key by parsing it.
|
||||
func ParsePrivateKeyBytes(content []byte) (*rsa.PrivateKey, error) {
|
||||
func ParsePrivateKeyBytes(content []byte) (crypto.Signer, error) {
|
||||
pemContent, _ := pem.Decode(content)
|
||||
if pemContent == nil {
|
||||
return nil, fmt.Errorf("no right PEM block")
|
||||
}
|
||||
|
||||
if pemContent.Type == "EC PRIVATE KEY" {
|
||||
privateKey, err := x509.ParseECPrivateKey(pemContent.Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse EC Private Key")
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
|
||||
privateKey, err := x509.ParsePKCS1PrivateKey(pemContent.Bytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot parse PKCS1 Private Key")
|
||||
@@ -128,7 +173,7 @@ func IsValidCertificateKeyPairBytes(certificateBytes []byte, privateKeyBytes []b
|
||||
switch {
|
||||
case !checkCertificateValidity(*crt):
|
||||
return false, nil
|
||||
case !checkPublicKeys(*crt.PublicKey.(*rsa.PublicKey), key.PublicKey): //nolint:forcetypeassert
|
||||
case !checkPublicKeys(crt.PublicKey, key):
|
||||
return false, nil
|
||||
default:
|
||||
return true, nil
|
||||
@@ -161,7 +206,7 @@ func VerifyCertificate(cert, ca []byte, usages ...x509.ExtKeyUsage) (bool, error
|
||||
return len(chains) > 0, err
|
||||
}
|
||||
|
||||
func generateCertificateKeyPairBytes(template *x509.Certificate, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*bytes.Buffer, *bytes.Buffer, error) {
|
||||
func generateCertificateKeyPairBytes(template *x509.Certificate, caCert *x509.Certificate, caKey crypto.Signer) (*bytes.Buffer, *bytes.Buffer, error) {
|
||||
certPrivKey, err := rsa.GenerateKey(cryptorand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, errors.Wrap(err, "cannot generate an RSA key")
|
||||
@@ -201,11 +246,12 @@ func checkCertificateValidity(cert x509.Certificate) bool {
|
||||
return notAfter && notBefore
|
||||
}
|
||||
|
||||
func checkPublicKeys(a rsa.PublicKey, b rsa.PublicKey) bool {
|
||||
isN := a.N.Cmp(b.N) == 0
|
||||
isE := a.E == b.E
|
||||
func checkPublicKeys(a crypto.PublicKey, b crypto.Signer) bool {
|
||||
if key, ok := a.(interface{ Equal(k crypto.PublicKey) bool }); ok {
|
||||
return key.Equal(b.Public())
|
||||
}
|
||||
|
||||
return isN && isE
|
||||
return false
|
||||
}
|
||||
|
||||
// NewCertificateTemplate returns the template that must be used to generate a certificate,
|
||||
|
||||
@@ -62,7 +62,7 @@ func (e *EtcdClient) GrantPrivileges(ctx context.Context, user, dbName string) e
|
||||
permission := etcdclient.PermissionType(authpb.READWRITE)
|
||||
key := e.buildKey(dbName)
|
||||
|
||||
if _, err := e.Client.RoleGrantPermission(ctx, user, key, etcdclient.GetPrefixRangeEnd(key), permission); err != nil {
|
||||
if _, err := e.Client.RoleGrantPermission(ctx, dbName, key, etcdclient.GetPrefixRangeEnd(key), permission); err != nil {
|
||||
return errors.NewGrantPrivilegesError(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,10 @@ import (
|
||||
|
||||
// CheckExists ensures that the default Datastore exists before starting the manager.
|
||||
func CheckExists(ctx context.Context, scheme *runtime.Scheme, datastoreName string) error {
|
||||
if datastoreName == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
ctrlClient, err := client.New(ctrl.GetConfigOrDie(), client.Options{Scheme: scheme})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create controlerruntime.Client: %w", err)
|
||||
|
||||
@@ -23,7 +23,7 @@ func BootstrapToken(client kubernetes.Interface, config *Configuration) error {
|
||||
return errors.Wrap(err, "error updating or creating token")
|
||||
}
|
||||
|
||||
if err := node.AllowBoostrapTokensToGetNodes(client); err != nil {
|
||||
if err := node.AllowBootstrapTokensToGetNodes(client); err != nil {
|
||||
return errors.Wrap(err, "error allowing bootstrap tokens to get Nodes")
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ func CreateKubeadmInitConfiguration(params Parameters) (*Configuration, error) {
|
||||
},
|
||||
}
|
||||
conf.Networking = kubeadmapi.Networking{
|
||||
DNSDomain: "cluster.local",
|
||||
DNSDomain: params.TenantControlPlaneClusterDomain,
|
||||
PodSubnet: params.TenantControlPlanePodCIDR,
|
||||
ServiceSubnet: params.TenantControlPlaneServiceCIDR,
|
||||
}
|
||||
|
||||
@@ -32,22 +32,23 @@ func (c *Configuration) Checksum() string {
|
||||
}
|
||||
|
||||
type Parameters struct {
|
||||
TenantControlPlaneName string
|
||||
TenantControlPlaneNamespace string
|
||||
TenantControlPlaneEndpoint string
|
||||
TenantControlPlaneAddress string
|
||||
TenantControlPlaneCertSANs []string
|
||||
TenantControlPlanePort int32
|
||||
TenantControlPlanePodCIDR string
|
||||
TenantControlPlaneServiceCIDR string
|
||||
TenantDNSServiceIPs []string
|
||||
TenantControlPlaneVersion string
|
||||
TenantControlPlaneCGroupDriver string
|
||||
ETCDs []string
|
||||
CertificatesDir string
|
||||
KubeconfigDir string
|
||||
KubeProxyOptions *AddonOptions
|
||||
CoreDNSOptions *AddonOptions
|
||||
TenantControlPlaneName string
|
||||
TenantControlPlaneNamespace string
|
||||
TenantControlPlaneEndpoint string
|
||||
TenantControlPlaneAddress string
|
||||
TenantControlPlaneCertSANs []string
|
||||
TenantControlPlanePort int32
|
||||
TenantControlPlaneClusterDomain string
|
||||
TenantControlPlanePodCIDR string
|
||||
TenantControlPlaneServiceCIDR string
|
||||
TenantDNSServiceIPs []string
|
||||
TenantControlPlaneVersion string
|
||||
TenantControlPlaneCGroupDriver string
|
||||
ETCDs []string
|
||||
CertificatesDir string
|
||||
KubeconfigDir string
|
||||
KubeProxyOptions *AddonOptions
|
||||
CoreDNSOptions *AddonOptions
|
||||
}
|
||||
|
||||
type AddonOptions struct {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/clastix/kamaji/internal/crypto"
|
||||
"github.com/clastix/kamaji/internal/kubeadm"
|
||||
"github.com/clastix/kamaji/internal/utilities"
|
||||
"github.com/clastix/kamaji/internal/webhook/handlers"
|
||||
)
|
||||
|
||||
type APIServerCertificate struct {
|
||||
@@ -66,6 +67,10 @@ func (r *APIServerCertificate) GetTmpDirectory() string {
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (res controllerutil.OperationResult, err error) {
|
||||
if err = (handlers.TenantControlPlaneCertSANs{}).ValidateCertSANs(tenantControlPlane); err != nil {
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
return utilities.CreateOrUpdateWithConflict(ctx, r.Client, r.resource, r.mutate(ctx, tenantControlPlane))
|
||||
}
|
||||
|
||||
@@ -84,6 +89,14 @@ func (r *APIServerCertificate) UpdateTenantControlPlaneStatus(_ context.Context,
|
||||
func (r *APIServerCertificate) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn {
|
||||
return func() error {
|
||||
logger := log.FromContext(ctx, "resource", r.GetName())
|
||||
// The Kubeadm configuration must be retrieved in advance:
|
||||
// this is required to check also the certificate SAN
|
||||
config, kadmErr := getStoredKubeadmConfiguration(ctx, r.Client, r.TmpDirectory, tenantControlPlane)
|
||||
if kadmErr != nil {
|
||||
logger.Error(kadmErr, "cannot retrieve stored kubeadm configuration", "err", kadmErr.Error())
|
||||
|
||||
return fmt.Errorf("failed to generate certificate and private key: %w", kadmErr)
|
||||
}
|
||||
// Retrieving the TenantControlPlane CA:
|
||||
// this is required to trigger a new generation in case of Certificate Authority rotation.
|
||||
namespacedName := k8stypes.NamespacedName{Namespace: tenantControlPlane.GetNamespace(), Name: tenantControlPlane.Status.Certificates.CA.SecretName}
|
||||
@@ -121,18 +134,23 @@ func (r *APIServerCertificate) mutate(ctx context.Context, tenantControlPlane *k
|
||||
logger.Info(fmt.Sprintf("%s certificate-private_key pair is not valid: %s", kubeadmconstants.APIServerCertAndKeyBaseName, err.Error()))
|
||||
}
|
||||
|
||||
if isCAValid && isCertValid {
|
||||
commonNames := config.InitConfiguration.APIServer.CertSANs
|
||||
|
||||
if tenantControlPlane.Spec.ControlPlane.Ingress != nil {
|
||||
address, _ := utilities.GetControlPlaneAddressAndPortFromHostname(tenantControlPlane.Spec.ControlPlane.Ingress.Hostname, 6443)
|
||||
commonNames = append(commonNames, address)
|
||||
}
|
||||
|
||||
dnsNamesMatches, dnsErr := crypto.CheckCertificateNamesAndIPs(r.resource.Data[kubeadmconstants.APIServerCertName], commonNames)
|
||||
if dnsErr != nil {
|
||||
logger.Info(fmt.Sprintf("%s SAN check returned an error: %s", kubeadmconstants.APIServerCertAndKeyBaseName, err.Error()))
|
||||
}
|
||||
|
||||
if isCAValid && isCertValid && dnsNamesMatches {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
config, err := getStoredKubeadmConfiguration(ctx, r.Client, r.TmpDirectory, tenantControlPlane)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot generate certificate and private key in api server certificate", "details", err.Error())
|
||||
|
||||
return fmt.Errorf("failed to generate certificate and private key: %w", err)
|
||||
}
|
||||
|
||||
ca := kubeadm.CertificatePrivateKeyPair{
|
||||
Name: kubeadmconstants.CACertAndKeyBaseName,
|
||||
Certificate: secretCA.Data[kubeadmconstants.CACertName],
|
||||
|
||||
@@ -128,8 +128,11 @@ func (d *Migrate) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamaji
|
||||
return resources.OperationResultEnqueueBack, nil
|
||||
case controllerutil.OperationResultNone:
|
||||
|
||||
if len(d.job.Status.Conditions) > 0 && d.job.Status.Conditions[0].Type == batchv1.JobComplete && d.job.Status.Conditions[0].Status == corev1.ConditionTrue {
|
||||
return controllerutil.OperationResultNone, nil
|
||||
// Note: job.Status.Conditions can contain more than one condition on Kubernetes versions greater than v1.30
|
||||
for _, condition := range d.job.Status.Conditions {
|
||||
if condition.Type == batchv1.JobComplete && condition.Status == corev1.ConditionTrue {
|
||||
return controllerutil.OperationResultNone, nil
|
||||
}
|
||||
}
|
||||
|
||||
d.inProgress = true
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/util/retry"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
@@ -47,7 +48,7 @@ func (r *Config) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (b
|
||||
func (r *Config) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
Name: utilities.AddTenantPrefix(r.GetName(), tenantControlPlane),
|
||||
Namespace: tenantControlPlane.GetNamespace(),
|
||||
},
|
||||
}
|
||||
@@ -55,10 +56,6 @@ func (r *Config) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.Te
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Config) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *Config) GetClient() client.Client {
|
||||
return r.Client
|
||||
}
|
||||
@@ -67,28 +64,32 @@ func (r *Config) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv
|
||||
return utilities.CreateOrUpdateWithConflict(ctx, r.Client, r.resource, r.mutate(ctx, tenantControlPlane))
|
||||
}
|
||||
|
||||
// Delete doesn't perform any deletion process: the Secret object has owner relationship
|
||||
// with the TenantControlPlane object, which has been previously deleted.
|
||||
func (r *Config) Delete(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) error {
|
||||
secret := r.resource.DeepCopy()
|
||||
|
||||
if err := r.Client.Get(ctx, types.NamespacedName{Name: r.resource.Name, Namespace: r.resource.Namespace}, secret); err != nil {
|
||||
if kubeerrors.IsNotFound(err) {
|
||||
return nil
|
||||
return retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
if err := r.Client.Get(ctx, types.NamespacedName{Name: r.resource.Name, Namespace: r.resource.Namespace}, secret); err != nil {
|
||||
if kubeerrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.Wrap(err, "cannot retrieve the DataStore Secret for removal")
|
||||
}
|
||||
|
||||
return errors.Wrap(err, "cannot retrieve the DataStore Secret for removal")
|
||||
}
|
||||
secret.SetFinalizers(nil)
|
||||
|
||||
secret.SetFinalizers(nil)
|
||||
if err := r.Client.Update(ctx, secret); err != nil {
|
||||
if kubeerrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.Client.Update(ctx, secret); err != nil {
|
||||
if kubeerrors.IsNotFound(err) {
|
||||
return nil
|
||||
return errors.Wrap(err, "cannot remove DataStore Secret finalizers")
|
||||
}
|
||||
|
||||
return errors.Wrap(err, "cannot remove DataStore Secret finalizers")
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (r *Config) GetName() string {
|
||||
@@ -155,9 +156,25 @@ func (r *Config) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.
|
||||
username = coalesceFn(tenantControlPlane.Status.Storage.Setup.User)
|
||||
}
|
||||
|
||||
var dataStoreSchema string
|
||||
switch {
|
||||
case len(tenantControlPlane.Status.Storage.Setup.Schema) > 0:
|
||||
// for existing TCPs, the dataStoreSchema will be adopted from the status,
|
||||
// as the mutating webhook only takes care of TCP creations, not updates
|
||||
dataStoreSchema = tenantControlPlane.Status.Storage.Setup.Schema
|
||||
tenantControlPlane.Spec.DataStoreSchema = dataStoreSchema
|
||||
case len(tenantControlPlane.Spec.DataStoreSchema) > 0:
|
||||
// for new TCPs, the spec field will have been provided by the user
|
||||
// or defaulted by the defaulting webhook
|
||||
dataStoreSchema = tenantControlPlane.Spec.DataStoreSchema
|
||||
default:
|
||||
// this can only happen on TCP creations when the webhook is not installed
|
||||
return fmt.Errorf("cannot build datastore storage config, schema name must either exist in Spec or Status")
|
||||
}
|
||||
|
||||
r.resource.Data = map[string][]byte{
|
||||
"DB_CONNECTION_STRING": []byte(r.ConnString),
|
||||
"DB_SCHEMA": coalesceFn(tenantControlPlane.Status.Storage.Setup.Schema),
|
||||
"DB_SCHEMA": []byte(dataStoreSchema),
|
||||
"DB_USER": username,
|
||||
"DB_PASSWORD": password,
|
||||
}
|
||||
|
||||
115
internal/resources/datastore/datastore_storage_config_test.go
Normal file
115
internal/resources/datastore/datastore_storage_config_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package datastore_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/resources"
|
||||
"github.com/clastix/kamaji/internal/resources/datastore"
|
||||
)
|
||||
|
||||
var _ = Describe("DatastoreStorageConfig", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
dsc *datastore.Config
|
||||
tcp *kamajiv1alpha1.TenantControlPlane
|
||||
ds *kamajiv1alpha1.DataStore
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = context.Background() //nolint:fatcontext
|
||||
|
||||
tcp = &kamajiv1alpha1.TenantControlPlane{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tcp",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: kamajiv1alpha1.TenantControlPlaneSpec{},
|
||||
}
|
||||
|
||||
ds = &kamajiv1alpha1.DataStore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "datastore",
|
||||
Namespace: "default",
|
||||
},
|
||||
}
|
||||
|
||||
Expect(kamajiv1alpha1.AddToScheme(scheme)).To(Succeed())
|
||||
Expect(corev1.AddToScheme(scheme)).To(Succeed())
|
||||
})
|
||||
|
||||
JustBeforeEach(func() {
|
||||
fakeClient = fake.NewClientBuilder().
|
||||
WithScheme(scheme).WithObjects(tcp).WithStatusSubresource(tcp).Build()
|
||||
|
||||
dsc = &datastore.Config{
|
||||
Client: fakeClient,
|
||||
ConnString: "",
|
||||
DataStore: *ds,
|
||||
}
|
||||
})
|
||||
|
||||
When("TCP has no dataStoreSchema defined", func() {
|
||||
It("should return an error", func() {
|
||||
_, err := resources.Handle(ctx, dsc, tcp)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
When("TCP has dataStoreSchema set in spec", func() {
|
||||
BeforeEach(func() {
|
||||
tcp.Spec.DataStoreSchema = "custom-prefix"
|
||||
})
|
||||
|
||||
It("should create the datastore secret with the schema name from the spec", func() {
|
||||
op, err := resources.Handle(ctx, dsc, tcp)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(op).To(Equal(controllerutil.OperationResultCreated))
|
||||
|
||||
secrets := &corev1.SecretList{}
|
||||
Expect(fakeClient.List(ctx, secrets)).To(Succeed())
|
||||
Expect(secrets.Items).To(HaveLen(1))
|
||||
Expect(secrets.Items[0].Data["DB_SCHEMA"]).To(Equal([]byte("custom-prefix")))
|
||||
})
|
||||
})
|
||||
|
||||
When("TCP has dataStoreSchema set in status, but not in spec", func() {
|
||||
// this test case ensures that existing TCPs (created in a CRD version without
|
||||
// the dataStoreSchema field) correctly adopt the spec field from the status.
|
||||
|
||||
It("should create the datastore secret with the correct schema name and update the TCP spec", func() {
|
||||
By("updating the TCP status")
|
||||
Expect(fakeClient.Get(ctx, client.ObjectKeyFromObject(tcp), tcp)).To(Succeed())
|
||||
tcp.Status.Storage.Setup.Schema = "existing-schema-name"
|
||||
Expect(fakeClient.Status().Update(ctx, tcp)).To(Succeed())
|
||||
|
||||
By("handling the resource")
|
||||
op, err := resources.Handle(ctx, dsc, tcp)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(op).To(Equal(controllerutil.OperationResultCreated))
|
||||
|
||||
By("checking the secret")
|
||||
secrets := &corev1.SecretList{}
|
||||
Expect(fakeClient.List(ctx, secrets)).To(Succeed())
|
||||
Expect(secrets.Items).To(HaveLen(1))
|
||||
Expect(secrets.Items[0].Data["DB_SCHEMA"]).To(Equal([]byte("existing-schema-name")))
|
||||
|
||||
By("checking the TCP spec")
|
||||
// we have to check the modified struct here (instead of retrieving the object
|
||||
// via the fakeClient), as the TCP resource update is not done by the resources.
|
||||
// Instead, the TCP controller will handle TCP updates after handling all resources
|
||||
tcp.Spec.DataStoreSchema = "existing-schema-name"
|
||||
})
|
||||
})
|
||||
})
|
||||
23
internal/resources/datastore/datastore_suite_test.go
Normal file
23
internal/resources/datastore/datastore_suite_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package datastore_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
var (
|
||||
fakeClient client.Client
|
||||
scheme *runtime.Scheme = runtime.NewScheme()
|
||||
)
|
||||
|
||||
func TestDatastore(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Datastore Suite")
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
@@ -106,6 +107,20 @@ func (r *KubernetesDeploymentResource) isProgressingUpgrade() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// An update is complete when new pods are ready and old pods deleted.
|
||||
desired := ptr.Deref(r.resource.Spec.Replicas, 2)
|
||||
if r.resource.Status.UpdatedReplicas != desired {
|
||||
return true
|
||||
}
|
||||
|
||||
if r.resource.Status.ReadyReplicas != desired {
|
||||
return true
|
||||
}
|
||||
|
||||
if r.resource.Status.Replicas != desired {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -80,10 +80,30 @@ func (r *KubernetesIngressResource) ShouldCleanup(tcp *kamajiv1alpha1.TenantCont
|
||||
return tcp.Spec.ControlPlane.Ingress == nil
|
||||
}
|
||||
|
||||
func (r *KubernetesIngressResource) CleanUp(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *KubernetesIngressResource) CleanUp(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
logger := log.FromContext(ctx, "resource", r.GetName())
|
||||
|
||||
if err := r.Client.Delete(ctx, r.resource); err != nil {
|
||||
var ingress networkingv1.Ingress
|
||||
if err := r.Client.Get(ctx, client.ObjectKey{
|
||||
Namespace: r.resource.GetNamespace(),
|
||||
Name: r.resource.GetName(),
|
||||
}, &ingress); err != nil {
|
||||
if !k8serrors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to get ingress resource before cleanup")
|
||||
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if !metav1.IsControlledBy(&ingress, tcp) {
|
||||
logger.Info("skipping cleanup: ingress is not managed by Kamaji", "name", ingress.Name, "namespace", ingress.Namespace)
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if err := r.Client.Delete(ctx, &ingress); err != nil {
|
||||
if !k8serrors.IsNotFound(err) {
|
||||
logger.Error(err, "cannot cleanup resource")
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/utilities"
|
||||
@@ -41,8 +41,6 @@ func (r *KubernetesServiceResource) CleanUp(context.Context, *kamajiv1alpha1.Ten
|
||||
}
|
||||
|
||||
func (r *KubernetesServiceResource) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
logger := log.FromContext(ctx, "resource", r.GetName())
|
||||
|
||||
tenantControlPlane.Status.Kubernetes.Service.ServiceStatus = r.resource.Status
|
||||
tenantControlPlane.Status.Kubernetes.Service.Name = r.resource.GetName()
|
||||
tenantControlPlane.Status.Kubernetes.Service.Namespace = r.resource.GetNamespace()
|
||||
@@ -50,8 +48,6 @@ func (r *KubernetesServiceResource) UpdateTenantControlPlaneStatus(ctx context.C
|
||||
|
||||
address, err := tenantControlPlane.DeclaredControlPlaneAddress(ctx, r.Client)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot retrieve Tenant Control Plane address")
|
||||
|
||||
return err
|
||||
}
|
||||
tenantControlPlane.Status.ControlPlaneEndpoint = net.JoinHostPort(address, strconv.FormatInt(int64(tenantControlPlane.Spec.NetworkProfile.Port), 10))
|
||||
@@ -103,8 +99,12 @@ func (r *KubernetesServiceResource) mutate(ctx context.Context, tenantControlPla
|
||||
case kamajiv1alpha1.ServiceTypeLoadBalancer:
|
||||
r.resource.Spec.Type = corev1.ServiceTypeLoadBalancer
|
||||
|
||||
if len(address) > 0 {
|
||||
r.resource.Spec.LoadBalancerIP = address
|
||||
if tenantControlPlane.Spec.NetworkProfile.LoadBalancerClass != nil {
|
||||
r.resource.Spec.LoadBalancerClass = ptr.To(*tenantControlPlane.Spec.NetworkProfile.LoadBalancerClass)
|
||||
}
|
||||
|
||||
if len(tenantControlPlane.Spec.NetworkProfile.LoadBalancerSourceRanges) > 0 {
|
||||
r.resource.Spec.LoadBalancerSourceRanges = tenantControlPlane.Spec.NetworkProfile.LoadBalancerSourceRanges
|
||||
}
|
||||
case kamajiv1alpha1.ServiceTypeNodePort:
|
||||
r.resource.Spec.Type = corev1.ServiceTypeNodePort
|
||||
|
||||
@@ -46,7 +46,7 @@ func (r *ServiceResource) ShouldStatusBeUpdated(_ context.Context, tenantControl
|
||||
return true
|
||||
}
|
||||
|
||||
for i := 0; i < len(resourceIngresses); i++ {
|
||||
for i := range resourceIngresses {
|
||||
if resourceIngresses[i].Hostname != statusIngresses[i].Hostname ||
|
||||
resourceIngresses[i].IP != statusIngresses[i].IP ||
|
||||
len(resourceIngresses[i].Ports) != len(statusIngresses[i].Ports) {
|
||||
@@ -55,7 +55,7 @@ func (r *ServiceResource) ShouldStatusBeUpdated(_ context.Context, tenantControl
|
||||
|
||||
resourcePorts := resourceIngresses[i].Ports
|
||||
statusPorts := statusIngresses[i].Ports
|
||||
for j := 0; j < len(resourcePorts); j++ {
|
||||
for j := range resourcePorts {
|
||||
if resourcePorts[j].Port != statusPorts[j].Port ||
|
||||
resourcePorts[j].Protocol != statusPorts[j].Protocol {
|
||||
return true
|
||||
|
||||
@@ -92,17 +92,18 @@ func (r *KubeadmConfigResource) mutate(ctx context.Context, tenantControlPlane *
|
||||
r.resource.SetLabels(utilities.KamajiLabels(tenantControlPlane.GetName(), r.GetName()))
|
||||
|
||||
params := kubeadm.Parameters{
|
||||
TenantControlPlaneAddress: address,
|
||||
TenantControlPlanePort: port,
|
||||
TenantControlPlaneName: tenantControlPlane.GetName(),
|
||||
TenantControlPlaneNamespace: tenantControlPlane.GetNamespace(),
|
||||
TenantControlPlaneEndpoint: r.getControlPlaneEndpoint(tenantControlPlane.Spec.ControlPlane.Ingress, address, port),
|
||||
TenantControlPlaneCertSANs: tenantControlPlane.Spec.NetworkProfile.CertSANs,
|
||||
TenantControlPlanePodCIDR: tenantControlPlane.Spec.NetworkProfile.PodCIDR,
|
||||
TenantControlPlaneServiceCIDR: tenantControlPlane.Spec.NetworkProfile.ServiceCIDR,
|
||||
TenantControlPlaneVersion: tenantControlPlane.Spec.Kubernetes.Version,
|
||||
ETCDs: r.ETCDs,
|
||||
CertificatesDir: r.TmpDirectory,
|
||||
TenantControlPlaneAddress: address,
|
||||
TenantControlPlanePort: port,
|
||||
TenantControlPlaneName: tenantControlPlane.GetName(),
|
||||
TenantControlPlaneNamespace: tenantControlPlane.GetNamespace(),
|
||||
TenantControlPlaneEndpoint: r.getControlPlaneEndpoint(tenantControlPlane.Spec.ControlPlane.Ingress, address, port),
|
||||
TenantControlPlaneCertSANs: tenantControlPlane.Spec.NetworkProfile.CertSANs,
|
||||
TenantControlPlaneClusterDomain: tenantControlPlane.Spec.NetworkProfile.ClusterDomain,
|
||||
TenantControlPlanePodCIDR: tenantControlPlane.Spec.NetworkProfile.PodCIDR,
|
||||
TenantControlPlaneServiceCIDR: tenantControlPlane.Spec.NetworkProfile.ServiceCIDR,
|
||||
TenantControlPlaneVersion: tenantControlPlane.Spec.Kubernetes.Version,
|
||||
ETCDs: r.ETCDs,
|
||||
CertificatesDir: r.TmpDirectory,
|
||||
}
|
||||
|
||||
config, err := kubeadm.CreateKubeadmInitConfiguration(params)
|
||||
|
||||
@@ -8,13 +8,14 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
bootstraptokenv1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/bootstraptoken/v1"
|
||||
bootstrapapi "k8s.io/cluster-bootstrap/token/api"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -23,7 +24,6 @@ import (
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/kubeadm"
|
||||
"github.com/clastix/kamaji/internal/resources/utils"
|
||||
)
|
||||
|
||||
type kubeadmPhase int
|
||||
@@ -52,7 +52,7 @@ func (r *KubeadmPhase) GetWatchedObject() client.Object {
|
||||
case PhaseUploadConfigKubelet:
|
||||
return &corev1.ConfigMap{}
|
||||
case PhaseBootstrapToken:
|
||||
return &corev1.Secret{}
|
||||
return &corev1.ConfigMap{}
|
||||
case PhaseClusterAdminRBAC:
|
||||
return &rbacv1.ClusterRoleBinding{}
|
||||
default:
|
||||
@@ -72,9 +72,9 @@ func (r *KubeadmPhase) GetPredicateFunc() func(obj client.Object) bool {
|
||||
}
|
||||
case PhaseBootstrapToken:
|
||||
return func(obj client.Object) bool {
|
||||
secret := obj.(*corev1.Secret) //nolint:forcetypeassert
|
||||
cm := obj.(*corev1.ConfigMap) //nolint:forcetypeassert
|
||||
|
||||
return secret.Type == "bootstrap.kubernetes.io/token" && secret.GetNamespace() == metav1.NamespaceSystem
|
||||
return cm.Name == bootstrapapi.ConfigMapClusterInfo && cm.GetNamespace() == metav1.NamespacePublic
|
||||
}
|
||||
case PhaseClusterAdminRBAC:
|
||||
return func(obj client.Object) bool {
|
||||
@@ -129,7 +129,7 @@ func (r *KubeadmPhase) GetKubeadmFunction(ctx context.Context, tcp *kamajiv1alph
|
||||
return kubeadm.UploadKubeletConfig, nil
|
||||
case PhaseBootstrapToken:
|
||||
return func(client clientset.Interface, config *kubeadm.Configuration) ([]byte, error) {
|
||||
bootstrapTokensEnrichment(config.InitConfiguration.BootstrapTokens)
|
||||
config.InitConfiguration.BootstrapTokens = nil
|
||||
|
||||
return nil, kubeadm.BootstrapToken(client, config)
|
||||
}, nil
|
||||
@@ -143,7 +143,6 @@ func (r *KubeadmPhase) GetKubeadmFunction(ctx context.Context, tcp *kamajiv1alph
|
||||
defer func() { _ = os.Remove(tmp) }()
|
||||
|
||||
var caSecret corev1.Secret
|
||||
|
||||
if err = r.Client.Get(ctx, types.NamespacedName{Name: tcp.Status.Certificates.CA.SecretName, Namespace: tcp.Namespace}, &caSecret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -164,7 +163,9 @@ func (r *KubeadmPhase) GetKubeadmFunction(ctx context.Context, tcp *kamajiv1alph
|
||||
_ = os.WriteFile(fmt.Sprintf("%s/%s", tmp, i), kubeconfigValue, os.ModePerm)
|
||||
}
|
||||
|
||||
if _, err = kubeconfig.EnsureAdminClusterRoleBinding(tmp, nil); err != nil {
|
||||
if _, err = kubeconfig.EnsureAdminClusterRoleBinding(tmp, func(_ context.Context, _ clientset.Interface, _ clientset.Interface, duration time.Duration, duration2 time.Duration) (clientset.Interface, error) {
|
||||
return kubeconfig.EnsureAdminClusterRoleBindingImpl(ctx, c, c, duration, duration2)
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -175,26 +176,6 @@ func (r *KubeadmPhase) GetKubeadmFunction(ctx context.Context, tcp *kamajiv1alph
|
||||
}
|
||||
}
|
||||
|
||||
func bootstrapTokensEnrichment(bootstrapTokens []bootstraptokenv1.BootstrapToken) {
|
||||
var bootstrapToken bootstraptokenv1.BootstrapToken
|
||||
if len(bootstrapTokens) > 0 {
|
||||
bootstrapToken = bootstrapTokens[0]
|
||||
}
|
||||
|
||||
enrichBootstrapToken(&bootstrapToken)
|
||||
bootstrapTokens[0] = bootstrapToken
|
||||
}
|
||||
|
||||
func enrichBootstrapToken(bootstrapToken *bootstraptokenv1.BootstrapToken) {
|
||||
if bootstrapToken.Token == nil {
|
||||
bootstrapToken.Token = &bootstraptokenv1.BootstrapTokenString{}
|
||||
}
|
||||
|
||||
if bootstrapToken.Token.ID == "" {
|
||||
bootstrapToken.Token.ID = fmt.Sprintf("%s.%s", utils.RandomString(6), utils.RandomString(16))
|
||||
}
|
||||
}
|
||||
|
||||
func (r *KubeadmPhase) GetClient() client.Client {
|
||||
return r.Client
|
||||
}
|
||||
@@ -227,7 +208,7 @@ func (r *KubeadmPhase) UpdateTenantControlPlaneStatus(ctx context.Context, tenan
|
||||
func (r *KubeadmPhase) GetStatus(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (kamajiv1alpha1.KubeadmConfigChecksumDependant, error) {
|
||||
switch r.Phase {
|
||||
case PhaseUploadConfigKubeadm, PhaseUploadConfigKubelet, PhaseClusterAdminRBAC:
|
||||
return nil, nil
|
||||
return nil, nil //nolint:nilnil
|
||||
case PhaseBootstrapToken:
|
||||
return &tenantControlPlane.Status.KubeadmPhase.BootstrapToken, nil
|
||||
default:
|
||||
@@ -238,5 +219,9 @@ func (r *KubeadmPhase) GetStatus(tenantControlPlane *kamajiv1alpha1.TenantContro
|
||||
func (r *KubeadmPhase) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
logger := log.FromContext(ctx, "resource", r.GetName(), "phase", r.Phase.String())
|
||||
|
||||
if r.Phase == PhaseBootstrapToken {
|
||||
return KubeadmBootstrap(ctx, r, logger, tenantControlPlane)
|
||||
}
|
||||
|
||||
return KubeadmPhaseCreate(ctx, r, logger, tenantControlPlane)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,12 @@ import (
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
bootstrapapi "k8s.io/cluster-bootstrap/token/api"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
@@ -81,6 +86,89 @@ func GetKubeadmManifestDeps(ctx context.Context, client client.Client, tenantCon
|
||||
return tenantClient, config, nil
|
||||
}
|
||||
|
||||
func KubeadmBootstrap(ctx context.Context, r KubeadmPhaseResource, logger logr.Logger, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
var checksum string
|
||||
|
||||
tntClient, err := utilities.GetTenantClient(ctx, r.GetClient(), tenantControlPlane)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot generate tenant client")
|
||||
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
var clusterInfo corev1.ConfigMap
|
||||
if cmErr := tntClient.Get(ctx, types.NamespacedName{Name: bootstrapapi.ConfigMapClusterInfo, Namespace: metav1.NamespacePublic}, &clusterInfo); cmErr != nil {
|
||||
if !k8serrors.IsNotFound(cmErr) {
|
||||
logger.Error(cmErr, "cannot retrieve cluster-info ConfigMap")
|
||||
}
|
||||
}
|
||||
|
||||
status, err := r.GetStatus(tenantControlPlane)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot retrieve status")
|
||||
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
if status != nil {
|
||||
checksum = utilities.CalculateMapChecksum(clusterInfo.Data)
|
||||
|
||||
if checksum == status.GetChecksum() {
|
||||
r.SetKubeadmConfigChecksum(checksum)
|
||||
|
||||
return controllerutil.OperationResultNone, nil
|
||||
}
|
||||
}
|
||||
|
||||
kubeconfig, err := utilities.GetTenantKubeconfig(ctx, r.GetClient(), tenantControlPlane)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot retrieve kubeconfig configuration")
|
||||
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
config, err := getStoredKubeadmConfiguration(ctx, r.GetClient(), r.GetTmpDirectory(), tenantControlPlane)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot retrieve kubeadm configuration")
|
||||
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
config.Kubeconfig = *kubeconfig
|
||||
|
||||
fun, err := r.GetKubeadmFunction(ctx, tenantControlPlane)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot retrieve kubeadm function")
|
||||
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
client, err := utilities.GetTenantClientSet(ctx, r.GetClient(), tenantControlPlane)
|
||||
if err != nil {
|
||||
logger.Error(err, "cannot generate tenant client")
|
||||
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
if _, err = fun(client, config); err != nil {
|
||||
logger.Error(err, "kubeadm function failed")
|
||||
|
||||
return controllerutil.OperationResultNone, err
|
||||
}
|
||||
|
||||
if status == nil {
|
||||
return controllerutil.OperationResultNone, nil
|
||||
}
|
||||
|
||||
r.SetKubeadmConfigChecksum(checksum)
|
||||
|
||||
if checksum == "" {
|
||||
return controllerutil.OperationResultCreated, nil
|
||||
}
|
||||
|
||||
return controllerutil.OperationResultUpdated, nil
|
||||
}
|
||||
|
||||
func KubeadmPhaseCreate(ctx context.Context, r KubeadmPhaseResource, logger logr.Logger, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
config, err := getStoredKubeadmConfiguration(ctx, r.GetClient(), r.GetTmpDirectory(), tenantControlPlane)
|
||||
if err != nil {
|
||||
|
||||
@@ -21,19 +21,19 @@ const (
|
||||
)
|
||||
|
||||
type Resource interface {
|
||||
Define(context.Context, *kamajiv1alpha1.TenantControlPlane) error
|
||||
ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool
|
||||
CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error)
|
||||
CreateOrUpdate(context.Context, *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error)
|
||||
Define(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) error
|
||||
ShouldCleanup(tcp *kamajiv1alpha1.TenantControlPlane) bool
|
||||
CleanUp(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) (bool, error)
|
||||
CreateOrUpdate(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error)
|
||||
GetName() string
|
||||
ShouldStatusBeUpdated(context.Context, *kamajiv1alpha1.TenantControlPlane) bool
|
||||
UpdateTenantControlPlaneStatus(context.Context, *kamajiv1alpha1.TenantControlPlane) error
|
||||
ShouldStatusBeUpdated(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) bool
|
||||
UpdateTenantControlPlaneStatus(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) error
|
||||
}
|
||||
|
||||
type DeletableResource interface {
|
||||
GetName() string
|
||||
Define(context.Context, *kamajiv1alpha1.TenantControlPlane) error
|
||||
Delete(context.Context, *kamajiv1alpha1.TenantControlPlane) error
|
||||
Define(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) error
|
||||
Delete(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) error
|
||||
}
|
||||
|
||||
type KubeadmResource interface {
|
||||
@@ -45,9 +45,9 @@ type KubeadmPhaseResource interface {
|
||||
Resource
|
||||
KubeadmResource
|
||||
GetClient() client.Client
|
||||
GetKubeadmFunction(context.Context, *kamajiv1alpha1.TenantControlPlane) (func(clientset.Interface, *kubeadm.Configuration) ([]byte, error), error)
|
||||
GetStatus(*kamajiv1alpha1.TenantControlPlane) (kamajiv1alpha1.KubeadmConfigChecksumDependant, error)
|
||||
SetKubeadmConfigChecksum(string)
|
||||
GetKubeadmFunction(ctx context.Context, tcp *kamajiv1alpha1.TenantControlPlane) (func(clientset.Interface, *kubeadm.Configuration) ([]byte, error), error)
|
||||
GetStatus(tcp *kamajiv1alpha1.TenantControlPlane) (kamajiv1alpha1.KubeadmConfigChecksumDependant, error)
|
||||
SetKubeadmConfigChecksum(checksum string)
|
||||
GetWatchedObject() client.Object
|
||||
GetPredicateFunc() func(obj client.Object) bool
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
package upgrade
|
||||
|
||||
const (
|
||||
KubeadmVersion = "v1.31.1"
|
||||
KubeadmVersion = "v1.32.3"
|
||||
)
|
||||
|
||||
@@ -33,7 +33,7 @@ func (d DataStoreSecretValidation) OnDelete(runtime.Object) AdmissionResponse {
|
||||
}
|
||||
|
||||
func (d DataStoreSecretValidation) OnUpdate(object runtime.Object, _ runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(ctx context.Context, _ admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
secret := object.(*corev1.Secret) //nolint:forcetypeassert
|
||||
|
||||
dsList := &kamajiv1alpha1.DataStoreList{}
|
||||
|
||||
@@ -25,7 +25,7 @@ type DataStoreValidation struct {
|
||||
}
|
||||
|
||||
func (d DataStoreValidation) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(ctx context.Context, _ admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
ds := object.(*kamajiv1alpha1.DataStore) //nolint:forcetypeassert
|
||||
|
||||
return nil, d.validate(ctx, *ds)
|
||||
@@ -33,7 +33,7 @@ func (d DataStoreValidation) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
}
|
||||
|
||||
func (d DataStoreValidation) OnDelete(object runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(ctx context.Context, _ admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
ds := object.(*kamajiv1alpha1.DataStore) //nolint:forcetypeassert
|
||||
|
||||
tcpList := &kamajiv1alpha1.TenantControlPlaneList{}
|
||||
@@ -50,7 +50,7 @@ func (d DataStoreValidation) OnDelete(object runtime.Object) AdmissionResponse {
|
||||
}
|
||||
|
||||
func (d DataStoreValidation) OnUpdate(object runtime.Object, oldObj runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(ctx context.Context, _ admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
newDs, oldDs := object.(*kamajiv1alpha1.DataStore), oldObj.(*kamajiv1alpha1.DataStore) //nolint:forcetypeassert
|
||||
|
||||
if oldDs.Spec.Driver != newDs.Spec.Driver {
|
||||
@@ -96,6 +96,10 @@ func (d DataStoreValidation) validateTLSConfig(ctx context.Context, ds kamajiv1a
|
||||
if ds.Spec.TLSConfig.CertificateAuthority.PrivateKey == nil {
|
||||
return fmt.Errorf("CA private key is required when using the etcd driver")
|
||||
}
|
||||
|
||||
if ds.Spec.TLSConfig.ClientCertificate == nil {
|
||||
return fmt.Errorf("client certificate is required when using the etcd driver")
|
||||
}
|
||||
}
|
||||
|
||||
if ds.Spec.TLSConfig.CertificateAuthority.PrivateKey != nil {
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
type AdmissionResponse func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error)
|
||||
|
||||
type Handler interface {
|
||||
OnCreate(runtime.Object) AdmissionResponse
|
||||
OnDelete(runtime.Object) AdmissionResponse
|
||||
OnCreate(obj runtime.Object) AdmissionResponse
|
||||
OnDelete(obj runtime.Object) AdmissionResponse
|
||||
OnUpdate(newObject runtime.Object, prevObject runtime.Object) AdmissionResponse
|
||||
}
|
||||
|
||||
16
internal/webhook/handlers/handlers_suite_test.go
Normal file
16
internal/webhook/handlers/handlers_suite_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestHandlers(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Handlers Suite")
|
||||
}
|
||||
51
internal/webhook/handlers/tcp_certsans.go
Normal file
51
internal/webhook/handlers/tcp_certsans.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/webhook/utils"
|
||||
)
|
||||
|
||||
type TenantControlPlaneCertSANs struct{}
|
||||
|
||||
func (t TenantControlPlaneCertSANs) ValidateCertSANs(tcp *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if len(tcp.Spec.NetworkProfile.CertSANs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := validation.ValidateCertSANs(tcp.Spec.NetworkProfile.CertSANs, field.NewPath("spec.networkProfile.certSANs")); err != nil {
|
||||
return err.ToAggregate()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneCertSANs) OnCreate(obj runtime.Object) AdmissionResponse {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := obj.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
return nil, t.ValidateCertSANs(tcp)
|
||||
}
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneCertSANs) OnDelete(runtime.Object) AdmissionResponse {
|
||||
return utils.NilOp()
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneCertSANs) OnUpdate(newObject runtime.Object, prevObject runtime.Object) AdmissionResponse {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := newObject.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
return nil, t.ValidateCertSANs(tcp)
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,14 @@ type TenantControlPlaneDataStore struct {
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneDataStore) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(ctx context.Context, _ admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
return nil, t.check(ctx, tcp.Spec.DataStore)
|
||||
if tcp.Spec.DataStore != "" {
|
||||
return nil, t.check(ctx, tcp.Spec.DataStore)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +39,14 @@ func (t TenantControlPlaneDataStore) OnDelete(runtime.Object) AdmissionResponse
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneDataStore) OnUpdate(object runtime.Object, _ runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(ctx context.Context, _ admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
return nil, t.check(ctx, tcp.Spec.DataStore)
|
||||
if tcp.Spec.DataStore != "" {
|
||||
return nil, t.check(ctx, tcp.Spec.DataStore)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ package handlers
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
@@ -22,25 +24,33 @@ type TenantControlPlaneDefaults struct {
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneDefaults) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
original := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
if len(tcp.Spec.DataStore) == 0 {
|
||||
operations, err := utils.JSONPatch(tcp, func() {
|
||||
tcp.Spec.DataStore = t.DefaultDatastore
|
||||
})
|
||||
defaulted := original.DeepCopy()
|
||||
t.defaultUnsetFields(defaulted)
|
||||
|
||||
if len(defaulted.Spec.NetworkProfile.DNSServiceIPs) == 0 {
|
||||
ip, _, err := net.ParseCIDR(defaulted.Spec.NetworkProfile.ServiceCIDR)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot create patch responses upon Tenant Control Plane creation")
|
||||
return nil, errors.Wrap(err, "cannot define resulting DNS Service IP")
|
||||
}
|
||||
switch {
|
||||
case ip.To4() != nil:
|
||||
ip[len(ip)-1] += 10
|
||||
case ip.To16() != nil:
|
||||
ip[len(ip)-1] += 16
|
||||
}
|
||||
|
||||
return operations, nil
|
||||
defaulted.Spec.NetworkProfile.DNSServiceIPs = []string{ip.String()}
|
||||
}
|
||||
|
||||
if tcp.Spec.ControlPlane.Deployment.Replicas == nil {
|
||||
tcp.Spec.ControlPlane.Deployment.Replicas = pointer.To(int32(2))
|
||||
operations, err := utils.JSONPatch(original, defaulted)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot create patch responses upon Tenant Control Plane creation")
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return operations, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,22 +58,22 @@ func (t TenantControlPlaneDefaults) OnDelete(runtime.Object) AdmissionResponse {
|
||||
return utils.NilOp()
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneDefaults) OnUpdate(object runtime.Object, oldObject runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
newTCP, oldTCP := object.(*kamajiv1alpha1.TenantControlPlane), oldObject.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
func (t TenantControlPlaneDefaults) OnUpdate(runtime.Object, runtime.Object) AdmissionResponse {
|
||||
// all immutability requirements are handled trough CEL annotations on the TenantControlPlaneSpec type
|
||||
return utils.NilOp()
|
||||
}
|
||||
|
||||
if oldTCP.Spec.DataStore == newTCP.Spec.DataStore {
|
||||
return nil, nil
|
||||
}
|
||||
func (t TenantControlPlaneDefaults) defaultUnsetFields(tcp *kamajiv1alpha1.TenantControlPlane) {
|
||||
if len(tcp.Spec.DataStore) == 0 && t.DefaultDatastore != "" {
|
||||
tcp.Spec.DataStore = t.DefaultDatastore
|
||||
}
|
||||
|
||||
if len(newTCP.Spec.DataStore) == 0 {
|
||||
return nil, fmt.Errorf("DataStore is a required field")
|
||||
}
|
||||
if tcp.Spec.ControlPlane.Deployment.Replicas == nil {
|
||||
tcp.Spec.ControlPlane.Deployment.Replicas = pointer.To(int32(2))
|
||||
}
|
||||
|
||||
if newTCP.Spec.ControlPlane.Deployment.Replicas == nil {
|
||||
newTCP.Spec.ControlPlane.Deployment.Replicas = pointer.To(int32(2))
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
if len(tcp.Spec.DataStoreSchema) == 0 {
|
||||
dss := strings.ReplaceAll(fmt.Sprintf("%s_%s", tcp.GetNamespace(), tcp.GetName()), "-", "_")
|
||||
tcp.Spec.DataStoreSchema = dss
|
||||
}
|
||||
}
|
||||
|
||||
78
internal/webhook/handlers/tcp_defaults_test.go
Normal file
78
internal/webhook/handlers/tcp_defaults_test.go
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/webhook/handlers"
|
||||
)
|
||||
|
||||
var _ = Describe("TCP Defaulting Webhook", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
t handlers.TenantControlPlaneDefaults
|
||||
tcp *kamajiv1alpha1.TenantControlPlane
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
t = handlers.TenantControlPlaneDefaults{
|
||||
DefaultDatastore: "etcd",
|
||||
}
|
||||
tcp = &kamajiv1alpha1.TenantControlPlane{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tcp",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: kamajiv1alpha1.TenantControlPlaneSpec{},
|
||||
}
|
||||
ctx = context.Background() //nolint:fatcontext
|
||||
})
|
||||
|
||||
Describe("fields missing", func() {
|
||||
It("should issue all required patches", func() {
|
||||
ops, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ops).To(HaveLen(3))
|
||||
})
|
||||
|
||||
It("should default the dataStore", func() {
|
||||
ops, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ops).To(ContainElement(
|
||||
jsonpatch.Operation{Operation: "add", Path: "/spec/dataStore", Value: "etcd"},
|
||||
))
|
||||
})
|
||||
|
||||
It("should default the dataStoreSchema to the expected value", func() {
|
||||
ops, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ops).To(ContainElement(
|
||||
jsonpatch.Operation{Operation: "add", Path: "/spec/dataStoreSchema", Value: "default_tcp"},
|
||||
))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("fields are already set", func() {
|
||||
BeforeEach(func() {
|
||||
tcp.Spec.DataStore = "etcd"
|
||||
tcp.Spec.DataStoreSchema = "my_tcp"
|
||||
tcp.Spec.ControlPlane.Deployment.Replicas = ptr.To(int32(2))
|
||||
})
|
||||
|
||||
It("should not issue any patches", func() {
|
||||
ops, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ops).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -54,7 +54,7 @@ func (t TenantControlPlaneDeployment) shouldTriggerCheck(newTCP, oldTCP kamajiv1
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneDeployment) OnUpdate(newObject runtime.Object, oldObject runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(ctx context.Context, _ admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp, previousTCP := newObject.(*kamajiv1alpha1.TenantControlPlane), oldObject.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
if !t.shouldTriggerCheck(*tcp, *previousTCP) {
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
type TenantControlPlaneKubeletAddresses struct{}
|
||||
|
||||
func (t TenantControlPlaneKubeletAddresses) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
return nil, t.validatePreferredKubeletAddressTypes(tcp.Spec.Kubernetes.Kubelet.PreferredAddressTypes)
|
||||
@@ -31,7 +31,7 @@ func (t TenantControlPlaneKubeletAddresses) OnDelete(runtime.Object) AdmissionRe
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneKubeletAddresses) OnUpdate(object runtime.Object, _ runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
return nil, t.validatePreferredKubeletAddressTypes(tcp.Spec.Kubernetes.Kubelet.PreferredAddressTypes)
|
||||
|
||||
58
internal/webhook/handlers/tcp_lb_src_ranges.go
Normal file
58
internal/webhook/handlers/tcp_lb_src_ranges.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"gomodules.xyz/jsonpatch/v2"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/webhook/utils"
|
||||
)
|
||||
|
||||
type TenantControlPlaneLoadBalancerSourceRanges struct{}
|
||||
|
||||
func (t TenantControlPlaneLoadBalancerSourceRanges) handle(tcp *kamajiv1alpha1.TenantControlPlane) error {
|
||||
for _, sourceCIDR := range tcp.Spec.NetworkProfile.LoadBalancerSourceRanges {
|
||||
_, _, err := net.ParseCIDR(sourceCIDR)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid LoadBalancer source CIDR %s, %s", sourceCIDR, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneLoadBalancerSourceRanges) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
if err := t.handle(tcp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneLoadBalancerSourceRanges) OnDelete(runtime.Object) AdmissionResponse {
|
||||
return utils.NilOp()
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneLoadBalancerSourceRanges) OnUpdate(object runtime.Object, _ runtime.Object) AdmissionResponse {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
if err := t.handle(tcp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
64
internal/webhook/handlers/tcp_lb_src_ranges_test.go
Normal file
64
internal/webhook/handlers/tcp_lb_src_ranges_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package handlers_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/webhook/handlers"
|
||||
)
|
||||
|
||||
var _ = Describe("TCP LoadBalancer Source Ranges Webhook", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
t handlers.TenantControlPlaneLoadBalancerSourceRanges
|
||||
tcp *kamajiv1alpha1.TenantControlPlane
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
t = handlers.TenantControlPlaneLoadBalancerSourceRanges{}
|
||||
tcp = &kamajiv1alpha1.TenantControlPlane{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "tcp",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: kamajiv1alpha1.TenantControlPlaneSpec{},
|
||||
}
|
||||
ctx = context.Background() //nolint:fatcontext
|
||||
})
|
||||
|
||||
It("allows creation when valid CIDR ranges are provided", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = kamajiv1alpha1.ServiceTypeLoadBalancer
|
||||
tcp.Spec.NetworkProfile.LoadBalancerSourceRanges = []string{"192.168.0.0/24"}
|
||||
_, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("allows creation when LoadBalancer service has no CIDR field", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = kamajiv1alpha1.ServiceTypeLoadBalancer
|
||||
_, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("allows creation when LoadBalancer service has an empty CIDR list", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = kamajiv1alpha1.ServiceTypeLoadBalancer
|
||||
tcp.Spec.NetworkProfile.LoadBalancerSourceRanges = []string{}
|
||||
_, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
It("denies creation when source ranges contain invalid CIDRs", func() {
|
||||
tcp.Spec.ControlPlane.Service.ServiceType = kamajiv1alpha1.ServiceTypeLoadBalancer
|
||||
tcp.Spec.NetworkProfile.LoadBalancerSourceRanges = []string{"192.168.0.0/33"}
|
||||
_, err := t.OnCreate(tcp)(ctx, admission.Request{})
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err.Error()).To(ContainSubstring("invalid LoadBalancer source CIDR 192.168.0.0/33"))
|
||||
})
|
||||
})
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
type TenantControlPlaneName struct{}
|
||||
|
||||
func (t TenantControlPlaneName) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
if errs := validation.IsDNS1035Label(tcp.Name); len(errs) > 0 {
|
||||
|
||||
@@ -59,7 +59,7 @@ func (t TenantControlPlaneServiceCIDR) OnDelete(runtime.Object) AdmissionRespons
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneServiceCIDR) OnUpdate(object runtime.Object, _ runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
if err := t.handle(tcp); err != nil {
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
type TenantControlPlaneVersion struct{}
|
||||
|
||||
func (t TenantControlPlaneVersion) OnCreate(object runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
tcp := object.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
ver, err := semver.New(t.normalizeKubernetesVersion(tcp.Spec.Kubernetes.Version))
|
||||
@@ -56,7 +56,7 @@ func (t TenantControlPlaneVersion) OnDelete(runtime.Object) AdmissionResponse {
|
||||
}
|
||||
|
||||
func (t TenantControlPlaneVersion) OnUpdate(object runtime.Object, oldObject runtime.Object) AdmissionResponse {
|
||||
return func(ctx context.Context, req admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
return func(context.Context, admission.Request) ([]jsonpatch.JsonPatchOperation, error) {
|
||||
newTCP, oldTCP := object.(*kamajiv1alpha1.TenantControlPlane), oldObject.(*kamajiv1alpha1.TenantControlPlane) //nolint:forcetypeassert
|
||||
|
||||
oldVer, oldErr := semver.Make(t.normalizeKubernetesVersion(oldTCP.Spec.Kubernetes.Version))
|
||||
|
||||
@@ -10,18 +10,16 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func JSONPatch(obj client.Object, modifierFunc func()) ([]jsonpatch.Operation, error) {
|
||||
original, err := json.Marshal(obj)
|
||||
func JSONPatch(original, modified client.Object) ([]jsonpatch.Operation, error) {
|
||||
originalJSON, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot marshal input object")
|
||||
return nil, errors.Wrap(err, "cannot marshal original object")
|
||||
}
|
||||
|
||||
modifierFunc()
|
||||
|
||||
patched, err := json.Marshal(obj)
|
||||
modifiedJSON, err := json.Marshal(modified)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot marshal patched object")
|
||||
return nil, errors.Wrap(err, "cannot marshal modified object")
|
||||
}
|
||||
|
||||
return jsonpatch.CreatePatch(original, patched)
|
||||
return jsonpatch.CreatePatch(originalJSON, modifiedJSON)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user