mirror of
https://github.com/clastix/kamaji.git
synced 2026-03-02 01:30:43 +00:00
Compare commits
10 Commits
helm-v0.1.
...
helm-v0.2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b23cbe3976 | ||
|
|
a23fcc502f | ||
|
|
baeee457a6 | ||
|
|
d6087949a9 | ||
|
|
ccb54b664c | ||
|
|
fdd1dd645e | ||
|
|
2c963881ab | ||
|
|
8e0b0c8ce7 | ||
|
|
fe231d6130 | ||
|
|
2ec6727de7 |
2
.github/workflows/helm.yaml
vendored
2
.github/workflows/helm.yaml
vendored
@@ -3,7 +3,7 @@ name: Helm Chart
|
||||
on:
|
||||
push:
|
||||
branches: [ "*" ]
|
||||
tags: [ "helm-v" ]
|
||||
tags: [ "helm-v*" ]
|
||||
pull_request:
|
||||
branches: [ "*" ]
|
||||
|
||||
|
||||
1
Makefile
1
Makefile
@@ -98,6 +98,7 @@ kustomize: ## Download kustomize locally if necessary.
|
||||
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
|
||||
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
cp config/crd/bases/kamaji.clastix.io_tenantcontrolplanes.yaml charts/kamaji/crds/tenantcontrolplane.yaml
|
||||
cp config/crd/bases/kamaji.clastix.io_datastores.yaml charts/kamaji/crds/datastore.yaml
|
||||
|
||||
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
|
||||
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
|
||||
|
||||
8
PROJECT
8
PROJECT
@@ -16,4 +16,12 @@ resources:
|
||||
kind: TenantControlPlane
|
||||
path: github.com/clastix/kamaji/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: false
|
||||
domain: clastix.io
|
||||
group: kamaji
|
||||
kind: DataStore
|
||||
path: github.com/clastix/kamaji/api/v1alpha1
|
||||
version: v1alpha1
|
||||
version: "3"
|
||||
|
||||
39
api/v1alpha1/datastore_funcs.go
Normal file
39
api/v1alpha1/datastore_funcs.go
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// GetContent is the resolver for the container of the Secret.
|
||||
// The bare content has priority over the external reference.
|
||||
func (in *ContentRef) GetContent(ctx context.Context, client client.Client) ([]byte, error) {
|
||||
if content := in.Content; len(content) > 0 {
|
||||
return content, nil
|
||||
}
|
||||
|
||||
secretRef := in.SecretRef
|
||||
|
||||
if secretRef == nil {
|
||||
return nil, fmt.Errorf("no bare content and no external Secret reference")
|
||||
}
|
||||
|
||||
secret, namespacedName := &corev1.Secret{}, types.NamespacedName{Name: secretRef.Name, Namespace: secretRef.Namespace}
|
||||
if err := client.Get(ctx, namespacedName, secret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v, ok := secret.Data[secretRef.KeyPath]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret %s does not have key %s", namespacedName.String(), secretRef.KeyPath)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
104
api/v1alpha1/datastore_types.go
Normal file
104
api/v1alpha1/datastore_types.go
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type Driver string //+kubebuilder:validation:Enum=etcd;MySQL;PostgreSQL
|
||||
|
||||
var (
|
||||
EtcdDriver Driver = "etcd"
|
||||
KineMySQLDriver Driver = "MySQL"
|
||||
KinePostgreSQLDriver Driver = "PostgreSQL"
|
||||
)
|
||||
|
||||
// DataStoreSpec defines the desired state of DataStore.
|
||||
type DataStoreSpec struct {
|
||||
// The driver to use to connect to the shared datastore.
|
||||
Driver Driver `json:"driver"`
|
||||
// List of the endpoints to connect to the shared datastore.
|
||||
// No need for protocol, just bare IP/FQDN and port.
|
||||
Endpoints []string `json:"endpoints"` //+kubebuilder:validation:MinLength=1
|
||||
// In case of authentication enabled for the given data store, specifies the username and password pair.
|
||||
// This value is optional.
|
||||
BasicAuth *BasicAuth `json:"basicAuth,omitempty"`
|
||||
// Defines the TLS/SSL configuration required to connect to the data store in a secure way.
|
||||
TLSConfig TLSConfig `json:"tlsConfig"`
|
||||
}
|
||||
|
||||
// TLSConfig contains the information used to connect to the data store using a secured connection.
|
||||
type TLSConfig struct {
|
||||
// Retrieve the Certificate Authority certificate and private key, such as bare content of the file, or a SecretReference.
|
||||
// The key reference is required since etcd authentication is based on certificates, and Kamaji is responsible in creating this.
|
||||
CertificateAuthority CertKeyPair `json:"certificateAuthority"`
|
||||
// Specifies the SSL/TLS key and private key pair used to connect to the data store.
|
||||
ClientCertificate ClientCertificate `json:"clientCertificate"`
|
||||
}
|
||||
|
||||
type ClientCertificate struct {
|
||||
Certificate ContentRef `json:"certificate"`
|
||||
PrivateKey ContentRef `json:"privateKey"`
|
||||
}
|
||||
|
||||
type CertKeyPair struct {
|
||||
Certificate ContentRef `json:"certificate"`
|
||||
PrivateKey *ContentRef `json:"privateKey,omitempty"`
|
||||
}
|
||||
|
||||
// BasicAuth contains the required information to perform the connection using user credentials to the data store.
|
||||
type BasicAuth struct {
|
||||
Username ContentRef `json:"username"`
|
||||
Password ContentRef `json:"password"`
|
||||
}
|
||||
|
||||
type ContentRef struct {
|
||||
// Bare content of the file, base64 encoded.
|
||||
// It has precedence over the SecretReference value.
|
||||
Content []byte `json:"content,omitempty"`
|
||||
SecretRef *SecretReference `json:"secretReference,omitempty"`
|
||||
}
|
||||
|
||||
type SecretReference struct {
|
||||
corev1.SecretReference `json:",inline"`
|
||||
// Name of the key for the given Secret reference where the content is stored.
|
||||
// This value is mandatory.
|
||||
KeyPath string `json:"keyPath"`
|
||||
}
|
||||
|
||||
// DataStoreStatus defines the observed state of DataStore.
|
||||
type DataStoreStatus struct {
|
||||
// List of the Tenant Control Planes, namespaced named, using this data store.
|
||||
UsedBy []string `json:"usedBy,omitempty"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
//+kubebuilder:subresource:status
|
||||
//+kubebuilder:resource:scope=Cluster
|
||||
//+kubebuilder:printcolumn:name="Driver",type="string",JSONPath=".spec.driver",description="Kamaji data store driver"
|
||||
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
|
||||
|
||||
// DataStore is the Schema for the datastores API.
|
||||
type DataStore struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec DataStoreSpec `json:"spec,omitempty"`
|
||||
Status DataStoreStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
//+kubebuilder:object:root=true
|
||||
|
||||
// DataStoreList contains a list of DataStore.
|
||||
type DataStoreList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []DataStore `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&DataStore{}, &DataStoreList{})
|
||||
}
|
||||
@@ -46,7 +46,7 @@ type PublicKeyPrivateKeyPairStatus struct {
|
||||
Checksum string `json:"checksum,omitempty"`
|
||||
}
|
||||
|
||||
// CertificatesStatus defines the observed state of ETCD Certificates.
|
||||
// CertificatesStatus defines the observed state of ETCD TLSConfig.
|
||||
type CertificatesStatus struct {
|
||||
CA CertificatePrivateKeyPairStatus `json:"ca,omitempty"`
|
||||
APIServer CertificatePrivateKeyPairStatus `json:"apiServer,omitempty"`
|
||||
|
||||
@@ -156,6 +156,44 @@ func (in AdmissionControllers) DeepCopy() AdmissionControllers {
|
||||
return *out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *BasicAuth) DeepCopyInto(out *BasicAuth) {
|
||||
*out = *in
|
||||
in.Username.DeepCopyInto(&out.Username)
|
||||
in.Password.DeepCopyInto(&out.Password)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BasicAuth.
|
||||
func (in *BasicAuth) DeepCopy() *BasicAuth {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(BasicAuth)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CertKeyPair) DeepCopyInto(out *CertKeyPair) {
|
||||
*out = *in
|
||||
in.Certificate.DeepCopyInto(&out.Certificate)
|
||||
if in.PrivateKey != nil {
|
||||
in, out := &in.PrivateKey, &out.PrivateKey
|
||||
*out = new(ContentRef)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CertKeyPair.
|
||||
func (in *CertKeyPair) DeepCopy() *CertKeyPair {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CertKeyPair)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CertificatePrivateKeyPairStatus) DeepCopyInto(out *CertificatePrivateKeyPairStatus) {
|
||||
*out = *in
|
||||
@@ -198,6 +236,48 @@ func (in *CertificatesStatus) DeepCopy() *CertificatesStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ClientCertificate) DeepCopyInto(out *ClientCertificate) {
|
||||
*out = *in
|
||||
in.Certificate.DeepCopyInto(&out.Certificate)
|
||||
in.PrivateKey.DeepCopyInto(&out.PrivateKey)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClientCertificate.
|
||||
func (in *ClientCertificate) DeepCopy() *ClientCertificate {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ClientCertificate)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ContentRef) DeepCopyInto(out *ContentRef) {
|
||||
*out = *in
|
||||
if in.Content != nil {
|
||||
in, out := &in.Content, &out.Content
|
||||
*out = make([]byte, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(SecretReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContentRef.
|
||||
func (in *ContentRef) DeepCopy() *ContentRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ContentRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ControlPlane) DeepCopyInto(out *ControlPlane) {
|
||||
*out = *in
|
||||
@@ -285,6 +365,111 @@ func (in *ControlPlaneExtraArgs) DeepCopy() *ControlPlaneExtraArgs {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DataStore) DeepCopyInto(out *DataStore) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataStore.
|
||||
func (in *DataStore) DeepCopy() *DataStore {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DataStore)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DataStore) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DataStoreList) DeepCopyInto(out *DataStoreList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]DataStore, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataStoreList.
|
||||
func (in *DataStoreList) DeepCopy() *DataStoreList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DataStoreList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DataStoreList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DataStoreSpec) DeepCopyInto(out *DataStoreSpec) {
|
||||
*out = *in
|
||||
if in.Endpoints != nil {
|
||||
in, out := &in.Endpoints, &out.Endpoints
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.BasicAuth != nil {
|
||||
in, out := &in.BasicAuth, &out.BasicAuth
|
||||
*out = new(BasicAuth)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.TLSConfig.DeepCopyInto(&out.TLSConfig)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataStoreSpec.
|
||||
func (in *DataStoreSpec) DeepCopy() *DataStoreSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DataStoreSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DataStoreStatus) DeepCopyInto(out *DataStoreStatus) {
|
||||
*out = *in
|
||||
if in.UsedBy != nil {
|
||||
in, out := &in.UsedBy, &out.UsedBy
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DataStoreStatus.
|
||||
func (in *DataStoreStatus) DeepCopy() *DataStoreStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DataStoreStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DeploymentSpec) DeepCopyInto(out *DeploymentSpec) {
|
||||
*out = *in
|
||||
@@ -768,6 +953,22 @@ func (in *SQLSetupStatus) DeepCopy() *SQLSetupStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SecretReference) DeepCopyInto(out *SecretReference) {
|
||||
*out = *in
|
||||
out.SecretReference = in.SecretReference
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference.
|
||||
func (in *SecretReference) DeepCopy() *SecretReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SecretReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) {
|
||||
*out = *in
|
||||
@@ -809,6 +1010,23 @@ func (in *StorageStatus) DeepCopy() *StorageStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TLSConfig) DeepCopyInto(out *TLSConfig) {
|
||||
*out = *in
|
||||
in.CertificateAuthority.DeepCopyInto(&out.CertificateAuthority)
|
||||
in.ClientCertificate.DeepCopyInto(&out.ClientCertificate)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSConfig.
|
||||
func (in *TLSConfig) DeepCopy() *TLSConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(TLSConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TenantControlPlane) DeepCopyInto(out *TenantControlPlane) {
|
||||
*out = *in
|
||||
|
||||
@@ -15,7 +15,7 @@ type: application
|
||||
# This is the chart version. This version number should be incremented each time you make changes
|
||||
# to the chart and its templates, including the app version.
|
||||
# Versions are expected to follow Semantic Versioning (https://semver.org/)
|
||||
version: 0.1.0
|
||||
version: 0.2.0
|
||||
|
||||
# This is the version number of the application being deployed. This version number should be
|
||||
# incremented each time you make changes to the application. Versions are not expected to
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# kamaji
|
||||
|
||||
  
|
||||
  
|
||||
|
||||
Kamaji is a tool aimed to build and operate a Managed Kubernetes Service with a fraction of the operational burden. With Kamaji, you can deploy and operate hundreds of Kubernetes clusters as a hyper-scaler.
|
||||
|
||||
@@ -68,6 +68,27 @@ Here the values you can override:
|
||||
|-----|------|---------|-------------|
|
||||
| affinity | object | `{}` | Kubernetes affinity rules to apply to Kamaji controller pods |
|
||||
| configPath | string | `"./kamaji.yaml"` | Configuration file path alternative. (default "./kamaji.yaml") |
|
||||
| datastore.basicAuth.passwordSecret.keyPath | string | `nil` | The Secret key where the data is stored. |
|
||||
| datastore.basicAuth.passwordSecret.name | string | `nil` | The name of the Secret containing the password used to connect to the relational database. |
|
||||
| datastore.basicAuth.passwordSecret.namespace | string | `nil` | The namespace of the Secret containing the password used to connect to the relational database. |
|
||||
| datastore.basicAuth.usernameSecret.keyPath | string | `nil` | The Secret key where the data is stored. |
|
||||
| datastore.basicAuth.usernameSecret.name | string | `nil` | The name of the Secret containing the username used to connect to the relational database. |
|
||||
| datastore.basicAuth.usernameSecret.namespace | string | `nil` | The namespace of the Secret containing the username used to connect to the relational database. |
|
||||
| datastore.driver | string | `"etcd"` | (string) The Kamaji Datastore driver, supported: etcd, MySQL, PostgreSQL (defaults=etcd). |
|
||||
| datastore.endpoints | list | `[]` | (array) List of endpoints of the selected Datastore. When letting the Chart install the etcd datastore, this field is populated automatically. |
|
||||
| datastore.nameOverride | string | `nil` | The Datastore name override, if empty defaults to `default` |
|
||||
| datastore.tlsConfig.certificateAuthority.certificate.keyPath | string | `nil` | Key of the Secret which contains the content of the certificate. |
|
||||
| datastore.tlsConfig.certificateAuthority.certificate.name | string | `nil` | Name of the Secret containing the CA required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| datastore.tlsConfig.certificateAuthority.certificate.namespace | string | `nil` | Namespace of the Secret containing the CA required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| datastore.tlsConfig.certificateAuthority.privateKey.keyPath | string | `nil` | Key of the Secret which contains the content of the private key. |
|
||||
| datastore.tlsConfig.certificateAuthority.privateKey.name | string | `nil` | Name of the Secret containing the CA private key required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| datastore.tlsConfig.certificateAuthority.privateKey.namespace | string | `nil` | Namespace of the Secret containing the CA private key required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| datastore.tlsConfig.clientCertificate.certificate.keyPath | string | `nil` | Key of the Secret which contains the content of the certificate. |
|
||||
| datastore.tlsConfig.clientCertificate.certificate.name | string | `nil` | Name of the Secret containing the client certificate required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| datastore.tlsConfig.clientCertificate.certificate.namespace | string | `nil` | Namespace of the Secret containing the client certificate required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| datastore.tlsConfig.clientCertificate.privateKey.keyPath | string | `nil` | Key of the Secret which contains the content of the private key. |
|
||||
| datastore.tlsConfig.clientCertificate.privateKey.name | string | `nil` | Name of the Secret containing the client certificate private key required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| datastore.tlsConfig.clientCertificate.privateKey.namespace | string | `nil` | Namespace of the Secret containing the client certificate private key required to establish the mandatory SSL/TLS connection to the datastore. |
|
||||
| etcd.compactionInterval | int | `0` | ETCD Compaction interval (e.g. "5m0s"). (default: "0" (disabled)) |
|
||||
| etcd.deploy | bool | `true` | Install an etcd with enabled multi-tenancy along with Kamaji |
|
||||
| etcd.image | object | `{"pullPolicy":"IfNotPresent","repository":"quay.io/coreos/etcd","tag":"v3.5.4"}` | Install specific etcd image |
|
||||
@@ -112,3 +133,9 @@ Here the values you can override:
|
||||
| serviceAccount.name | string | `"kamaji-controller-manager"` | |
|
||||
| temporaryDirectoryPath | string | `"/tmp/kamaji"` | Directory which will be used to work with temporary files. (default "/tmp/kamaji") |
|
||||
| tolerations | list | `[]` | Kubernetes node taints that the Kamaji controller pods would tolerate |
|
||||
|
||||
## Installing and managing etcd as DataStore
|
||||
|
||||
Kamaji supports multiple data store, although `etcd` is the default one: thus, an initial cluster will be created upon the Chart installation.
|
||||
|
||||
The `DataStore` resource can be configured with the proper values in case of overrides when using a different driver, otherwise all the required data will be inherited by the Chart values.
|
||||
|
||||
@@ -54,3 +54,9 @@ If you only need to make minor customizations, you can specify them on the comma
|
||||
Here the values you can override:
|
||||
|
||||
{{ template "chart.valuesSection" . }}
|
||||
|
||||
## Installing and managing etcd as DataStore
|
||||
|
||||
Kamaji supports multiple data store, although `etcd` is the default one: thus, an initial cluster will be created upon the Chart installation.
|
||||
|
||||
The `DataStore` resource can be configured with the proper values in case of overrides when using a different driver, otherwise all the required data will be inherited by the Chart values.
|
||||
|
||||
269
charts/kamaji/crds/datastore.yaml
Normal file
269
charts/kamaji/crds/datastore.yaml
Normal file
@@ -0,0 +1,269 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.1
|
||||
creationTimestamp: null
|
||||
name: datastores.kamaji.clastix.io
|
||||
spec:
|
||||
group: kamaji.clastix.io
|
||||
names:
|
||||
kind: DataStore
|
||||
listKind: DataStoreList
|
||||
plural: datastores
|
||||
singular: datastore
|
||||
scope: Cluster
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- description: Kamaji data store driver
|
||||
jsonPath: .spec.driver
|
||||
name: Driver
|
||||
type: string
|
||||
- description: Age
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: DataStore is the Schema for the datastores API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: DataStoreSpec defines the desired state of DataStore.
|
||||
properties:
|
||||
basicAuth:
|
||||
description: In case of authentication enabled for the given data
|
||||
store, specifies the username and password pair. This value is optional.
|
||||
properties:
|
||||
password:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded. It
|
||||
has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret reference
|
||||
where the content is stored. This value is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to reference
|
||||
a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
username:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded. It
|
||||
has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret reference
|
||||
where the content is stored. This value is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to reference
|
||||
a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- password
|
||||
- username
|
||||
type: object
|
||||
driver:
|
||||
description: The driver to use to connect to the shared datastore.
|
||||
type: string
|
||||
endpoints:
|
||||
description: List of the endpoints to connect to the shared datastore.
|
||||
No need for protocol, just bare IP/FQDN and port.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
tlsConfig:
|
||||
description: Defines the TLS/SSL configuration required to connect
|
||||
to the data store in a secure way.
|
||||
properties:
|
||||
certificateAuthority:
|
||||
description: Retrieve the Certificate Authority certificate and
|
||||
private key, such as bare content of the file, or a SecretReference.
|
||||
The key reference is required since etcd authentication is based
|
||||
on certificates, and Kamaji is responsible in creating this.
|
||||
properties:
|
||||
certificate:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
privateKey:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- certificate
|
||||
type: object
|
||||
clientCertificate:
|
||||
description: Specifies the SSL/TLS key and private key pair used
|
||||
to connect to the data store.
|
||||
properties:
|
||||
certificate:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
privateKey:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- certificate
|
||||
- privateKey
|
||||
type: object
|
||||
required:
|
||||
- certificateAuthority
|
||||
- clientCertificate
|
||||
type: object
|
||||
required:
|
||||
- driver
|
||||
- endpoints
|
||||
- tlsConfig
|
||||
type: object
|
||||
status:
|
||||
description: DataStoreStatus defines the observed state of DataStore.
|
||||
properties:
|
||||
usedBy:
|
||||
description: List of the Tenant Control Planes, namespaced named,
|
||||
using this data store.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
90
charts/kamaji/templates/_helpers_datastore.tpl
Normal file
90
charts/kamaji/templates/_helpers_datastore.tpl
Normal file
@@ -0,0 +1,90 @@
|
||||
{{/*
|
||||
Create a default fully qualified datastore name.
|
||||
*/}}
|
||||
{{- define "datastore.fullname" -}}
|
||||
{{- default "default" .Values.datastore.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "datastore.labels" -}}
|
||||
kamaji.clastix.io/datastore: {{ .Values.datastore.driver }}
|
||||
helm.sh/chart: {{ include "kamaji.chart" . }}
|
||||
{{ include "kamaji.selectorLabels" . }}
|
||||
{{- if .Chart.AppVersion }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
||||
{{- end }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Datastore endpoints, in case of ETCD, retrieving the one provided by the chart.
|
||||
*/}}
|
||||
{{- define "datastore.endpoints" -}}
|
||||
{{- if eq .Values.datastore.driver "etcd" }}
|
||||
{{ include "etcd.endpoints" . }}
|
||||
{{- else }}
|
||||
{{ .Values.datastore.endpoints }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
The Certificate Authority section for the DataSource object.
|
||||
*/}}
|
||||
{{- define "datastore.certificateAuthority" -}}
|
||||
{{- if eq .Values.datastore.driver "etcd" }}
|
||||
certificate:
|
||||
secretReference:
|
||||
name: {{ include "etcd.caSecretName" . }}
|
||||
namespace: {{ include "etcd.caSecretNamespace" . }}
|
||||
keyPath: ca.crt
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: {{ include "etcd.caSecretName" . }}
|
||||
namespace: {{ include "etcd.caSecretNamespace" . }}
|
||||
keyPath: ca.key
|
||||
{{- else }}
|
||||
certificate:
|
||||
secretReference:
|
||||
name: {{ .Values.datastore.tlsConfig.certificateAuthority.certificate.name }}
|
||||
namespace: {{ .Values.datastore.tlsConfig.certificateAuthority.certificate.namespace }}
|
||||
keyPath: {{ .Values.datastore.tlsConfig.certificateAuthority.certificate.keyPath }}
|
||||
{{- if .Values.datastore.tlsConfig.certificateAuthority.privateKey.name }}
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: {{ .Values.datastore.tlsConfig.certificateAuthority.privateKey.name }}
|
||||
namespace: {{ .Values.datastore.tlsConfig.certificateAuthority.privateKey.namespace }}
|
||||
keyPath: {{ .Values.datastore.tlsConfig.certificateAuthority.privateKey.keyPath }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
The Client Certificate section for the DataSource object.
|
||||
*/}}
|
||||
{{- define "datastore.clientCertificate" -}}
|
||||
{{- if eq .Values.datastore.driver "etcd" }}
|
||||
certificate:
|
||||
secretReference:
|
||||
name: {{ include "etcd.clientSecretName" . }}
|
||||
namespace: {{ include "etcd.clientSecretNamespace" . }}
|
||||
keyPath: tls.crt
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: {{ include "etcd.clientSecretName" . }}
|
||||
namespace: {{ include "etcd.clientSecretNamespace" . }}
|
||||
keyPath: tls.key
|
||||
{{- else }}
|
||||
certificate:
|
||||
secretReference:
|
||||
name: {{ .Values.datastore.tlsConfig.clientCertificate.certificate.name }}
|
||||
namespace: {{ .Values.datastore.tlsConfig.clientCertificate.certificate.namespace }}
|
||||
keyPath: {{ .Values.datastore.tlsConfig.clientCertificate.certificate.keyPath }}
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: {{ .Values.datastore.tlsConfig.clientCertificate.privateKey.name }}
|
||||
namespace: {{ .Values.datastore.tlsConfig.clientCertificate.privateKey.namespace }}
|
||||
keyPath: {{ .Values.datastore.tlsConfig.clientCertificate.privateKey.keyPath }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -108,7 +108,7 @@ Comma separated list of etcd endpoints, using the overrides in case of unmanaged
|
||||
{{- else if not .Values.etcd.overrides.endpoints }}
|
||||
{{- fail "A valid .Values.etcd.overrides.endpoints required!" }}
|
||||
{{- end }}
|
||||
{{- join "," $list -}}
|
||||
{{- $list | toYaml }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
|
||||
@@ -40,16 +40,11 @@ spec:
|
||||
protocol: TCP
|
||||
- args:
|
||||
- --config-file={{ .Values.configPath }}
|
||||
- --etcd-ca-secret-name={{ include "etcd.caSecretName" . }}
|
||||
- --etcd-ca-secret-namespace={{ include "etcd.caSecretNamespace" . }}
|
||||
- --etcd-client-secret-name={{ include "etcd.clientSecretName" . }}
|
||||
- --etcd-client-secret-namespace={{ include "etcd.clientSecretNamespace" . }}
|
||||
- --etcd-compaction-interval={{ .Values.etcd.compactionInterval }}
|
||||
- --etcd-endpoints={{ include "etcd.endpoints" . }}
|
||||
- --health-probe-bind-address={{ .Values.healthProbeBindAddress }}
|
||||
- --leader-elect
|
||||
- --metrics-bind-address={{ .Values.metricsBindAddress }}
|
||||
- --tmp-directory={{ .Values.temporaryDirectoryPath }}
|
||||
- --datastore={{ include "datastore.fullname" . }}
|
||||
{{- if .Values.loggingDevel.enable }}
|
||||
- --zap-devel
|
||||
{{- end }}
|
||||
|
||||
19
charts/kamaji/templates/datastore.yaml
Normal file
19
charts/kamaji/templates/datastore.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: DataStore
|
||||
metadata:
|
||||
name: {{ include "datastore.fullname" . }}
|
||||
labels:
|
||||
{{- include "datastore.labels" . | nindent 4 }}
|
||||
spec:
|
||||
driver: {{ .Values.datastore.driver }}
|
||||
endpoints:
|
||||
{{- include "datastore.endpoints" . | indent 4 }}
|
||||
{{- if (and .Values.datastore.basicAuth.usernameSecret.name .Values.datastore.basicAuth.passwordSecret.name) }}
|
||||
basicAuth:
|
||||
{{- .Values.datastore.basicAuth | toYaml | nindent 4 }}
|
||||
{{- end }}
|
||||
tlsConfig:
|
||||
certificateAuthority:
|
||||
{{- include "datastore.certificateAuthority" . | indent 6 }}
|
||||
clientCertificate:
|
||||
{{- include "datastore.clientCertificate" . | indent 6 }}
|
||||
@@ -102,6 +102,32 @@ rules:
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
|
||||
@@ -154,3 +154,57 @@ temporaryDirectoryPath: "/tmp/kamaji"
|
||||
loggingDevel:
|
||||
# -- (string) Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error) (default false)
|
||||
enable: false
|
||||
|
||||
datastore:
|
||||
# -- (string) The Datastore name override, if empty defaults to `default`
|
||||
nameOverride:
|
||||
# -- (string) The Kamaji Datastore driver, supported: etcd, MySQL, PostgreSQL (defaults=etcd).
|
||||
driver: etcd
|
||||
# -- (array) List of endpoints of the selected Datastore. When letting the Chart install the etcd datastore, this field is populated automatically.
|
||||
endpoints: []
|
||||
basicAuth:
|
||||
usernameSecret:
|
||||
# -- The name of the Secret containing the username used to connect to the relational database.
|
||||
name:
|
||||
# -- The namespace of the Secret containing the username used to connect to the relational database.
|
||||
namespace:
|
||||
# -- The Secret key where the data is stored.
|
||||
keyPath:
|
||||
passwordSecret:
|
||||
# -- The name of the Secret containing the password used to connect to the relational database.
|
||||
name:
|
||||
# -- The namespace of the Secret containing the password used to connect to the relational database.
|
||||
namespace:
|
||||
# -- The Secret key where the data is stored.
|
||||
keyPath:
|
||||
tlsConfig:
|
||||
certificateAuthority:
|
||||
certificate:
|
||||
# -- Name of the Secret containing the CA required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
name:
|
||||
# -- Namespace of the Secret containing the CA required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
namespace:
|
||||
# -- Key of the Secret which contains the content of the certificate.
|
||||
keyPath:
|
||||
privateKey:
|
||||
# -- Name of the Secret containing the CA private key required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
name:
|
||||
# -- Namespace of the Secret containing the CA private key required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
namespace:
|
||||
# -- Key of the Secret which contains the content of the private key.
|
||||
keyPath:
|
||||
clientCertificate:
|
||||
certificate:
|
||||
# -- Name of the Secret containing the client certificate required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
name:
|
||||
# -- Namespace of the Secret containing the client certificate required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
namespace:
|
||||
# -- Key of the Secret which contains the content of the certificate.
|
||||
keyPath:
|
||||
privateKey:
|
||||
# -- Name of the Secret containing the client certificate private key required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
name:
|
||||
# -- Namespace of the Secret containing the client certificate private key required to establish the mandatory SSL/TLS connection to the datastore.
|
||||
namespace:
|
||||
# -- Key of the Secret which contains the content of the private key.
|
||||
keyPath:
|
||||
|
||||
269
config/crd/bases/kamaji.clastix.io_datastores.yaml
Normal file
269
config/crd/bases/kamaji.clastix.io_datastores.yaml
Normal file
@@ -0,0 +1,269 @@
|
||||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.6.1
|
||||
creationTimestamp: null
|
||||
name: datastores.kamaji.clastix.io
|
||||
spec:
|
||||
group: kamaji.clastix.io
|
||||
names:
|
||||
kind: DataStore
|
||||
listKind: DataStoreList
|
||||
plural: datastores
|
||||
singular: datastore
|
||||
scope: Cluster
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- description: Kamaji data store driver
|
||||
jsonPath: .spec.driver
|
||||
name: Driver
|
||||
type: string
|
||||
- description: Age
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: DataStore is the Schema for the datastores API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: DataStoreSpec defines the desired state of DataStore.
|
||||
properties:
|
||||
basicAuth:
|
||||
description: In case of authentication enabled for the given data
|
||||
store, specifies the username and password pair. This value is optional.
|
||||
properties:
|
||||
password:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded. It
|
||||
has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret reference
|
||||
where the content is stored. This value is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to reference
|
||||
a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
username:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded. It
|
||||
has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret reference
|
||||
where the content is stored. This value is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to reference
|
||||
a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- password
|
||||
- username
|
||||
type: object
|
||||
driver:
|
||||
description: The driver to use to connect to the shared datastore.
|
||||
type: string
|
||||
endpoints:
|
||||
description: List of the endpoints to connect to the shared datastore.
|
||||
No need for protocol, just bare IP/FQDN and port.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
tlsConfig:
|
||||
description: Defines the TLS/SSL configuration required to connect
|
||||
to the data store in a secure way.
|
||||
properties:
|
||||
certificateAuthority:
|
||||
description: Retrieve the Certificate Authority certificate and
|
||||
private key, such as bare content of the file, or a SecretReference.
|
||||
The key reference is required since etcd authentication is based
|
||||
on certificates, and Kamaji is responsible in creating this.
|
||||
properties:
|
||||
certificate:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
privateKey:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- certificate
|
||||
type: object
|
||||
clientCertificate:
|
||||
description: Specifies the SSL/TLS key and private key pair used
|
||||
to connect to the data store.
|
||||
properties:
|
||||
certificate:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
privateKey:
|
||||
properties:
|
||||
content:
|
||||
description: Bare content of the file, base64 encoded.
|
||||
It has precedence over the SecretReference value.
|
||||
format: byte
|
||||
type: string
|
||||
secretReference:
|
||||
properties:
|
||||
keyPath:
|
||||
description: Name of the key for the given Secret
|
||||
reference where the content is stored. This value
|
||||
is mandatory.
|
||||
type: string
|
||||
name:
|
||||
description: Name is unique within a namespace to
|
||||
reference a secret resource.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace defines the space within which
|
||||
the secret name must be unique.
|
||||
type: string
|
||||
required:
|
||||
- keyPath
|
||||
type: object
|
||||
type: object
|
||||
required:
|
||||
- certificate
|
||||
- privateKey
|
||||
type: object
|
||||
required:
|
||||
- certificateAuthority
|
||||
- clientCertificate
|
||||
type: object
|
||||
required:
|
||||
- driver
|
||||
- endpoints
|
||||
- tlsConfig
|
||||
type: object
|
||||
status:
|
||||
description: DataStoreStatus defines the observed state of DataStore.
|
||||
properties:
|
||||
usedBy:
|
||||
description: List of the Tenant Control Planes, namespaced named,
|
||||
using this data store.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
||||
7
config/crd/patches/cainjection_in_datastores.yaml
Normal file
7
config/crd/patches/cainjection_in_datastores.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
# The following patch adds a directive for certmanager to inject CA into the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
|
||||
name: datastores.kamaji.clastix.io
|
||||
16
config/crd/patches/webhook_in_datastores.yaml
Normal file
16
config/crd/patches/webhook_in_datastores.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
# The following patch enables a conversion webhook for the CRD
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: datastores.kamaji.clastix.io
|
||||
spec:
|
||||
conversion:
|
||||
strategy: Webhook
|
||||
webhook:
|
||||
clientConfig:
|
||||
service:
|
||||
namespace: system
|
||||
name: webhook-service
|
||||
path: /convert
|
||||
conversionReviewVersions:
|
||||
- v1
|
||||
@@ -16,6 +16,7 @@ bases:
|
||||
- ../crd
|
||||
- ../rbac
|
||||
- ../manager
|
||||
- ../samples
|
||||
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
|
||||
# crd/kustomization.yaml
|
||||
#- ../webhook
|
||||
|
||||
@@ -1233,6 +1233,26 @@ rules:
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
@@ -1438,3 +1458,39 @@ spec:
|
||||
runAsNonRoot: true
|
||||
serviceAccountName: kamaji-controller-manager
|
||||
terminationGracePeriodSeconds: 10
|
||||
---
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: DataStore
|
||||
metadata:
|
||||
name: kamaji-etcd
|
||||
namespace: kamaji-system
|
||||
spec:
|
||||
basicAuth: null
|
||||
driver: etcd
|
||||
endpoints:
|
||||
- etcd-0.etcd.kamaji-system.svc:2379
|
||||
- etcd-1.etcd.kamaji-system.svc:2379
|
||||
- etcd-2.etcd.kamaji-system.svc:2379
|
||||
tlsConfig:
|
||||
certificateAuthority:
|
||||
certificate:
|
||||
secretReference:
|
||||
keyPath: ca.crt
|
||||
name: etcd-certs
|
||||
namespace: kamaji-system
|
||||
privateKey:
|
||||
secretReference:
|
||||
keyPath: ca.key
|
||||
name: etcd-certs
|
||||
namespace: kamaji-system
|
||||
clientCertificate:
|
||||
certificate:
|
||||
secretReference:
|
||||
keyPath: tls.crt
|
||||
name: root-client-certs
|
||||
namespace: kamaji-system
|
||||
privateKey:
|
||||
secretReference:
|
||||
keyPath: tls.key
|
||||
name: root-client-certs
|
||||
namespace: kamaji-system
|
||||
|
||||
24
config/rbac/datastore_editor_role.yaml
Normal file
24
config/rbac/datastore_editor_role.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
# permissions for end users to edit datastores.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: datastore-editor-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores/status
|
||||
verbs:
|
||||
- get
|
||||
20
config/rbac/datastore_viewer_role.yaml
Normal file
20
config/rbac/datastore_viewer_role.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
# permissions for end users to view datastores.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: datastore-viewer-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores/status
|
||||
verbs:
|
||||
- get
|
||||
@@ -54,6 +54,26 @@ rules:
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
- datastores/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- kamaji.clastix.io
|
||||
resources:
|
||||
|
||||
34
config/samples/kamaji_v1alpha1_datastore_etcd.yaml
Normal file
34
config/samples/kamaji_v1alpha1_datastore_etcd.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: DataStore
|
||||
metadata:
|
||||
name: etcd
|
||||
spec:
|
||||
driver: etcd
|
||||
endpoints:
|
||||
- etcd-0.etcd.kamaji-system.svc:2379
|
||||
- etcd-1.etcd.kamaji-system.svc:2379
|
||||
- etcd-2.etcd.kamaji-system.svc:2379
|
||||
basicAuth: null
|
||||
tlsConfig:
|
||||
certificateAuthority:
|
||||
certificate:
|
||||
secretReference:
|
||||
name: etcd-certs
|
||||
namespace: kamaji-system
|
||||
keyPath: "ca.crt"
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: etcd-certs
|
||||
namespace: kamaji-system
|
||||
keyPath: "ca.key"
|
||||
clientCertificate:
|
||||
certificate:
|
||||
secretReference:
|
||||
name: root-client-certs
|
||||
namespace: kamaji-system
|
||||
keyPath: "tls.crt"
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: root-client-certs
|
||||
namespace: kamaji-system
|
||||
keyPath: "tls.key"
|
||||
34
config/samples/kamaji_v1alpha1_datastore_mysql.yaml
Normal file
34
config/samples/kamaji_v1alpha1_datastore_mysql.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: DataStore
|
||||
metadata:
|
||||
name: mysql
|
||||
spec:
|
||||
driver: MySQL
|
||||
endpoints:
|
||||
- mariadb.kamaji-system.svc:3306
|
||||
basicAuth:
|
||||
username:
|
||||
content: cm9vdA==
|
||||
password:
|
||||
secretReference:
|
||||
name: mysql-config
|
||||
namespace: kamaji-system
|
||||
keyPath: MYSQL_ROOT_PASSWORD
|
||||
tlsConfig:
|
||||
certificateAuthority:
|
||||
certificate:
|
||||
secretReference:
|
||||
name: mysql-config
|
||||
namespace: kamaji-system
|
||||
keyPath: "ca.crt"
|
||||
clientCertificate:
|
||||
certificate:
|
||||
secretReference:
|
||||
name: mysql-config
|
||||
namespace: kamaji-system
|
||||
keyPath: "server.crt"
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: mysql-config
|
||||
namespace: kamaji-system
|
||||
keyPath: "server.key"
|
||||
37
config/samples/kamaji_v1alpha1_datastore_postgresql.yaml
Normal file
37
config/samples/kamaji_v1alpha1_datastore_postgresql.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: kamaji.clastix.io/v1alpha1
|
||||
kind: DataStore
|
||||
metadata:
|
||||
name: postgresql
|
||||
spec:
|
||||
driver: PostgreSQL
|
||||
endpoints:
|
||||
- postgresql-rw.kamaji-system.svc:5432
|
||||
basicAuth:
|
||||
username:
|
||||
secretReference:
|
||||
name: postgresql-superuser
|
||||
namespace: kamaji-system
|
||||
keyPath: username
|
||||
password:
|
||||
secretReference:
|
||||
name: postgresql-superuser
|
||||
namespace: kamaji-system
|
||||
keyPath: password
|
||||
tlsConfig:
|
||||
certificateAuthority:
|
||||
certificate:
|
||||
secretReference:
|
||||
name: postgresql-ca
|
||||
namespace: kamaji-system
|
||||
keyPath: ca.crt
|
||||
clientCertificate:
|
||||
certificate:
|
||||
secretReference:
|
||||
name: postgres-root-cert
|
||||
namespace: kamaji-system
|
||||
keyPath: tls.crt
|
||||
privateKey:
|
||||
secretReference:
|
||||
name: postgres-root-cert
|
||||
namespace: kamaji-system
|
||||
keyPath: tls.key
|
||||
@@ -1,4 +1,4 @@
|
||||
## Append samples you want in your CSV to this file as resources ##
|
||||
resources:
|
||||
- kamaji_v1alpha1_tenantcontrolplane.yaml
|
||||
- kamaji_v1alpha1_datastore_etcd.yaml
|
||||
#+kubebuilder:scaffold:manifestskustomizesamples
|
||||
|
||||
96
controllers/datastore_controller.go
Normal file
96
controllers/datastore_controller.go
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
controllerruntime "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
)
|
||||
|
||||
type DataStore struct {
|
||||
client client.Client
|
||||
// TenantControlPlaneTrigger is the channel used to communicate across the controllers:
|
||||
// if a Data Source is updated we have to be sure that the reconciliation of the certificates content
|
||||
// for each Tenant Control Plane is put in place properly.
|
||||
TenantControlPlaneTrigger TenantControlPlaneChannel
|
||||
// ResourceName is the DataStore object that should be watched for changes.
|
||||
ResourceName string
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=kamaji.clastix.io,resources=datastores,verbs=get;list;watch;create;update;patch;delete
|
||||
//+kubebuilder:rbac:groups=kamaji.clastix.io,resources=datastores/status,verbs=get;update;patch
|
||||
|
||||
func (r *DataStore) Reconcile(ctx context.Context, request reconcile.Request) (reconcile.Result, error) {
|
||||
ds := kamajiv1alpha1.DataStore{}
|
||||
if err := r.client.Get(ctx, request.NamespacedName, &ds); err != nil {
|
||||
if k8serrors.IsNotFound(err) {
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
// A Data Source can trigger several Tenant Control Planes and requires a minimum validation:
|
||||
// we have to ensure the data provided by the Data Source is valid and referencing an existing Secret object.
|
||||
if _, err := ds.Spec.TLSConfig.CertificateAuthority.Certificate.GetContent(ctx, r.client); err != nil {
|
||||
return reconcile.Result{}, errors.Wrap(err, "invalid Certificate Authority data")
|
||||
}
|
||||
|
||||
if _, err := ds.Spec.TLSConfig.ClientCertificate.Certificate.GetContent(ctx, r.client); err != nil {
|
||||
return reconcile.Result{}, errors.Wrap(err, "invalid Client Certificate data")
|
||||
}
|
||||
|
||||
if _, err := ds.Spec.TLSConfig.ClientCertificate.PrivateKey.GetContent(ctx, r.client); err != nil {
|
||||
return reconcile.Result{}, errors.Wrap(err, "invalid Client Certificate data")
|
||||
}
|
||||
|
||||
tcpList := kamajiv1alpha1.TenantControlPlaneList{}
|
||||
|
||||
if err := r.client.List(ctx, &tcpList); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
// Updating the status with the list of Tenant Control Plane using the following Data Source
|
||||
tcpSets := sets.NewString()
|
||||
for _, tcp := range tcpList.Items {
|
||||
tcpSets.Insert(getNamespacedName(tcp.GetNamespace(), tcp.GetName()).String())
|
||||
}
|
||||
|
||||
ds.Status.UsedBy = tcpSets.List()
|
||||
|
||||
if err := r.client.Status().Update(ctx, &ds); err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
// Triggering the reconciliation of the Tenant Control Plane upon a Secret change
|
||||
for _, i := range tcpList.Items {
|
||||
tcp := i
|
||||
|
||||
r.TenantControlPlaneTrigger <- event.GenericEvent{Object: &tcp}
|
||||
}
|
||||
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *DataStore) InjectClient(client client.Client) error {
|
||||
r.client = client
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DataStore) SetupWithManager(mgr controllerruntime.Manager) error {
|
||||
return controllerruntime.NewControllerManagedBy(mgr).
|
||||
For(&kamajiv1alpha1.DataStore{}, builder.WithPredicates(predicate.NewPredicateFuncs(func(object client.Object) bool {
|
||||
return object.GetName() == r.ResourceName
|
||||
}))).
|
||||
Complete(r)
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/google/uuid"
|
||||
@@ -15,11 +15,6 @@ import (
|
||||
"github.com/clastix/kamaji/internal/resources"
|
||||
"github.com/clastix/kamaji/internal/resources/konnectivity"
|
||||
"github.com/clastix/kamaji/internal/sql"
|
||||
"github.com/clastix/kamaji/internal/types"
|
||||
)
|
||||
|
||||
const (
|
||||
separator = ","
|
||||
)
|
||||
|
||||
type GroupResourceBuilderConfiguration struct {
|
||||
@@ -28,6 +23,7 @@ type GroupResourceBuilderConfiguration struct {
|
||||
tcpReconcilerConfig TenantControlPlaneReconcilerConfig
|
||||
tenantControlPlane kamajiv1alpha1.TenantControlPlane
|
||||
DBConnection sql.DBConnection
|
||||
DataStore kamajiv1alpha1.DataStore
|
||||
}
|
||||
|
||||
type GroupDeleteableResourceBuilderConfiguration struct {
|
||||
@@ -41,25 +37,25 @@ type GroupDeleteableResourceBuilderConfiguration struct {
|
||||
// GetResources returns a list of resources that will be used to provide tenant control planes
|
||||
// Currently there is only a default approach
|
||||
// TODO: the idea of this function is to become a factory to return the group of resources according to the given configuration.
|
||||
func GetResources(config GroupResourceBuilderConfiguration) []resources.Resource {
|
||||
return getDefaultResources(config)
|
||||
func GetResources(config GroupResourceBuilderConfiguration, dataStore kamajiv1alpha1.DataStore) []resources.Resource {
|
||||
return getDefaultResources(config, dataStore)
|
||||
}
|
||||
|
||||
// GetDeletableResources returns a list of resources that have to be deleted when tenant control planes are deleted
|
||||
// Currently there is only a default approach
|
||||
// TODO: the idea of this function is to become a factory to return the group of deleteable resources according to the given configuration.
|
||||
func GetDeletableResources(config GroupDeleteableResourceBuilderConfiguration) []resources.DeleteableResource {
|
||||
return getDefaultDeleteableResources(config)
|
||||
func GetDeletableResources(config GroupDeleteableResourceBuilderConfiguration, dataStore kamajiv1alpha1.DataStore) []resources.DeleteableResource {
|
||||
return getDefaultDeleteableResources(config, dataStore)
|
||||
}
|
||||
|
||||
func getDefaultResources(config GroupResourceBuilderConfiguration) []resources.Resource {
|
||||
func getDefaultResources(config GroupResourceBuilderConfiguration, dataStore kamajiv1alpha1.DataStore) []resources.Resource {
|
||||
resources := append(getUpgradeResources(config.client, config.tenantControlPlane), getKubernetesServiceResources(config.client, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubeadmConfigResources(config.client, config.tcpReconcilerConfig, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubeadmConfigResources(config.client, getTmpDirectory(config.tcpReconcilerConfig.TmpBaseDirectory, config.tenantControlPlane), dataStore)...)
|
||||
resources = append(resources, getKubernetesCertificatesResources(config.client, config.log, config.tcpReconcilerConfig, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubeconfigResources(config.client, config.log, config.tcpReconcilerConfig, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubernetesStorageResources(config.client, config.log, config.tcpReconcilerConfig, config.DBConnection, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubernetesStorageResources(config.client, config.log, config.tcpReconcilerConfig, config.DBConnection, config.tenantControlPlane, dataStore)...)
|
||||
resources = append(resources, getInternalKonnectivityResources(config.client, config.log, config.tcpReconcilerConfig, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubernetesDeploymentResources(config.client, config.tcpReconcilerConfig, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubernetesDeploymentResources(config.client, config.tcpReconcilerConfig, dataStore)...)
|
||||
resources = append(resources, getKubernetesIngressResources(config.client, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubeadmPhaseResources(config.client, config.log, config.tenantControlPlane)...)
|
||||
resources = append(resources, getKubeadmAddonResources(config.client, config.log, config.tenantControlPlane)...)
|
||||
@@ -68,24 +64,20 @@ func getDefaultResources(config GroupResourceBuilderConfiguration) []resources.R
|
||||
return resources
|
||||
}
|
||||
|
||||
func getDefaultDeleteableResources(config GroupDeleteableResourceBuilderConfiguration) []resources.DeleteableResource {
|
||||
switch config.tcpReconcilerConfig.ETCDStorageType {
|
||||
case types.ETCD:
|
||||
func getDefaultDeleteableResources(config GroupDeleteableResourceBuilderConfiguration, dataStore kamajiv1alpha1.DataStore) []resources.DeleteableResource {
|
||||
switch dataStore.Spec.Driver {
|
||||
case kamajiv1alpha1.EtcdDriver:
|
||||
return []resources.DeleteableResource{
|
||||
&resources.ETCDSetupResource{
|
||||
Name: "etcd-setup",
|
||||
Client: config.client,
|
||||
Log: config.log,
|
||||
ETCDClientCertsSecret: getNamespacedName(config.tcpReconcilerConfig.ETCDClientSecretNamespace, config.tcpReconcilerConfig.ETCDClientSecretName),
|
||||
ETCDCACertsSecret: getNamespacedName(config.tcpReconcilerConfig.ETCDCASecretNamespace, config.tcpReconcilerConfig.ETCDCASecretName),
|
||||
Endpoints: getArrayFromString(config.tcpReconcilerConfig.ETCDEndpoints),
|
||||
Client: config.client,
|
||||
Log: config.log,
|
||||
DataStore: dataStore,
|
||||
},
|
||||
}
|
||||
case types.KineMySQL, types.KinePostgreSQL:
|
||||
case kamajiv1alpha1.KineMySQLDriver, kamajiv1alpha1.KinePostgreSQLDriver:
|
||||
return []resources.DeleteableResource{
|
||||
&resources.SQLSetup{
|
||||
Client: config.client,
|
||||
Name: "sql-setup",
|
||||
DBConnection: config.DBConnection,
|
||||
},
|
||||
}
|
||||
@@ -97,7 +89,6 @@ func getDefaultDeleteableResources(config GroupDeleteableResourceBuilderConfigur
|
||||
func getUpgradeResources(c client.Client, tenantControlPlane kamajiv1alpha1.TenantControlPlane) []resources.Resource {
|
||||
return []resources.Resource{
|
||||
&resources.KubernetesUpgrade{
|
||||
Name: "upgrade",
|
||||
Client: c,
|
||||
},
|
||||
}
|
||||
@@ -111,14 +102,21 @@ func getKubernetesServiceResources(c client.Client, tenantControlPlane kamajiv1a
|
||||
}
|
||||
}
|
||||
|
||||
func getKubeadmConfigResources(c client.Client, tcpReconcilerConfig TenantControlPlaneReconcilerConfig, tenantControlPlane kamajiv1alpha1.TenantControlPlane) []resources.Resource {
|
||||
func getKubeadmConfigResources(c client.Client, tmpDirectory string, dataStore kamajiv1alpha1.DataStore) []resources.Resource {
|
||||
var endpoints []string
|
||||
|
||||
switch dataStore.Spec.Driver {
|
||||
case kamajiv1alpha1.EtcdDriver:
|
||||
endpoints = dataStore.Spec.Endpoints
|
||||
default:
|
||||
endpoints = []string{"127.0.0.1:2379"}
|
||||
}
|
||||
|
||||
return []resources.Resource{
|
||||
&resources.KubeadmConfigResource{
|
||||
Name: "kubeadmconfig",
|
||||
ETCDs: getArrayFromString(tcpReconcilerConfig.ETCDEndpoints),
|
||||
ETCDCompactionInterval: tcpReconcilerConfig.ETCDCompactionInterval,
|
||||
Client: c,
|
||||
TmpDirectory: getTmpDirectory(tcpReconcilerConfig.TmpBaseDirectory, tenantControlPlane),
|
||||
ETCDs: endpoints,
|
||||
Client: c,
|
||||
TmpDirectory: tmpDirectory,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -126,37 +124,31 @@ func getKubeadmConfigResources(c client.Client, tcpReconcilerConfig TenantContro
|
||||
func getKubernetesCertificatesResources(c client.Client, log logr.Logger, tcpReconcilerConfig TenantControlPlaneReconcilerConfig, tenantControlPlane kamajiv1alpha1.TenantControlPlane) []resources.Resource {
|
||||
return []resources.Resource{
|
||||
&resources.CACertificate{
|
||||
Name: "ca",
|
||||
Client: c,
|
||||
Log: log,
|
||||
TmpDirectory: getTmpDirectory(tcpReconcilerConfig.TmpBaseDirectory, tenantControlPlane),
|
||||
},
|
||||
&resources.FrontProxyCACertificate{
|
||||
Name: "front-proxy-ca-certificate",
|
||||
Client: c,
|
||||
Log: log,
|
||||
TmpDirectory: getTmpDirectory(tcpReconcilerConfig.TmpBaseDirectory, tenantControlPlane),
|
||||
},
|
||||
&resources.SACertificate{
|
||||
Name: "sa-certificate",
|
||||
Client: c,
|
||||
Log: log,
|
||||
TmpDirectory: getTmpDirectory(tcpReconcilerConfig.TmpBaseDirectory, tenantControlPlane),
|
||||
},
|
||||
&resources.APIServerCertificate{
|
||||
Name: "api-server-certificate",
|
||||
Client: c,
|
||||
Log: log,
|
||||
TmpDirectory: getTmpDirectory(tcpReconcilerConfig.TmpBaseDirectory, tenantControlPlane),
|
||||
},
|
||||
&resources.APIServerKubeletClientCertificate{
|
||||
Name: "api-server-kubelet-client-certificate",
|
||||
Client: c,
|
||||
Log: log,
|
||||
TmpDirectory: getTmpDirectory(tcpReconcilerConfig.TmpBaseDirectory, tenantControlPlane),
|
||||
},
|
||||
&resources.FrontProxyClientCertificate{
|
||||
Name: "front-proxy-client-certificate",
|
||||
Client: c,
|
||||
Log: log,
|
||||
TmpDirectory: getTmpDirectory(tcpReconcilerConfig.TmpBaseDirectory, tenantControlPlane),
|
||||
@@ -190,16 +182,15 @@ func getKubeconfigResources(c client.Client, log logr.Logger, tcpReconcilerConfi
|
||||
}
|
||||
}
|
||||
|
||||
func getKubernetesStorageResources(c client.Client, log logr.Logger, tcpReconcilerConfig TenantControlPlaneReconcilerConfig, dbConnection sql.DBConnection, tenantControlPlane kamajiv1alpha1.TenantControlPlane) []resources.Resource {
|
||||
switch tcpReconcilerConfig.ETCDStorageType {
|
||||
case types.ETCD:
|
||||
func getKubernetesStorageResources(c client.Client, log logr.Logger, tcpReconcilerConfig TenantControlPlaneReconcilerConfig, dbConnection sql.DBConnection, tenantControlPlane kamajiv1alpha1.TenantControlPlane, ds kamajiv1alpha1.DataStore) []resources.Resource {
|
||||
switch ds.Spec.Driver {
|
||||
case kamajiv1alpha1.EtcdDriver:
|
||||
return []resources.Resource{
|
||||
&resources.ETCDCACertificatesResource{
|
||||
Name: "etcd-ca-certificates",
|
||||
Client: c,
|
||||
Log: log,
|
||||
ETCDCASecretName: tcpReconcilerConfig.ETCDCASecretName,
|
||||
ETCDCASecretNamespace: tcpReconcilerConfig.ETCDCASecretNamespace,
|
||||
Name: "etcd-ca-certificates",
|
||||
Client: c,
|
||||
Log: log,
|
||||
DataStore: ds,
|
||||
},
|
||||
&resources.ETCDCertificatesResource{
|
||||
Name: "etcd-certificates",
|
||||
@@ -207,15 +198,12 @@ func getKubernetesStorageResources(c client.Client, log logr.Logger, tcpReconcil
|
||||
Log: log,
|
||||
},
|
||||
&resources.ETCDSetupResource{
|
||||
Name: "etcd-setup",
|
||||
Client: c,
|
||||
Log: log,
|
||||
ETCDClientCertsSecret: getNamespacedName(tcpReconcilerConfig.ETCDClientSecretNamespace, tcpReconcilerConfig.ETCDClientSecretName),
|
||||
ETCDCACertsSecret: getNamespacedName(tcpReconcilerConfig.ETCDCASecretNamespace, tcpReconcilerConfig.ETCDCASecretName),
|
||||
Endpoints: getArrayFromString(tcpReconcilerConfig.ETCDEndpoints),
|
||||
Client: c,
|
||||
Log: log,
|
||||
DataStore: ds,
|
||||
},
|
||||
}
|
||||
case types.KineMySQL, types.KinePostgreSQL:
|
||||
case kamajiv1alpha1.KineMySQLDriver, kamajiv1alpha1.KinePostgreSQLDriver:
|
||||
return []resources.Resource{
|
||||
&resources.SQLStorageConfig{
|
||||
Client: c,
|
||||
@@ -226,16 +214,12 @@ func getKubernetesStorageResources(c client.Client, log logr.Logger, tcpReconcil
|
||||
},
|
||||
&resources.SQLSetup{
|
||||
Client: c,
|
||||
Name: "sql-setup",
|
||||
DBConnection: dbConnection,
|
||||
Driver: dbConnection.Driver(),
|
||||
},
|
||||
&resources.SQLCertificate{
|
||||
Client: c,
|
||||
Name: "sql-certificate",
|
||||
StorageType: tcpReconcilerConfig.ETCDStorageType,
|
||||
SQLConfigSecretName: tcpReconcilerConfig.KineSecretName,
|
||||
SQLConfigSecretNamespace: tcpReconcilerConfig.KineSecretNamespace,
|
||||
Client: c,
|
||||
DataStore: ds,
|
||||
},
|
||||
}
|
||||
default:
|
||||
@@ -243,14 +227,22 @@ func getKubernetesStorageResources(c client.Client, log logr.Logger, tcpReconcil
|
||||
}
|
||||
}
|
||||
|
||||
func getKubernetesDeploymentResources(c client.Client, tcpReconcilerConfig TenantControlPlaneReconcilerConfig, tenantControlPlane kamajiv1alpha1.TenantControlPlane) []resources.Resource {
|
||||
func getKubernetesDeploymentResources(c client.Client, tcpReconcilerConfig TenantControlPlaneReconcilerConfig, dataStore kamajiv1alpha1.DataStore) []resources.Resource {
|
||||
var endpoints []string
|
||||
|
||||
switch dataStore.Spec.Driver {
|
||||
case kamajiv1alpha1.EtcdDriver:
|
||||
endpoints = dataStore.Spec.Endpoints
|
||||
default:
|
||||
endpoints = []string{"127.0.0.1:2379"}
|
||||
}
|
||||
|
||||
return []resources.Resource{
|
||||
&resources.KubernetesDeploymentResource{
|
||||
Client: c,
|
||||
ETCDEndpoints: getArrayFromString(tcpReconcilerConfig.ETCDEndpoints),
|
||||
ETCDCompactionInterval: tcpReconcilerConfig.ETCDCompactionInterval,
|
||||
ETCDStorageType: tcpReconcilerConfig.ETCDStorageType,
|
||||
KineContainerImage: tcpReconcilerConfig.KineContainerImage,
|
||||
Client: c,
|
||||
ETCDEndpoints: endpoints,
|
||||
DataStoreDriver: dataStore.Spec.Driver,
|
||||
KineContainerImage: tcpReconcilerConfig.KineContainerImage,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -346,13 +338,6 @@ func getInternalKonnectivityResources(c client.Client, log logr.Logger, tcpRecon
|
||||
}
|
||||
}
|
||||
|
||||
func getArrayFromString(s string) []string {
|
||||
var a []string
|
||||
a = append(a, strings.Split(s, separator)...)
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
func getNamespacedName(namespace string, name string) k8stypes.NamespacedName {
|
||||
return k8stypes.NamespacedName{Namespace: namespace, Name: name}
|
||||
}
|
||||
|
||||
@@ -8,75 +8,92 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"strings"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/sql"
|
||||
"github.com/clastix/kamaji/internal/types"
|
||||
)
|
||||
|
||||
func (r *TenantControlPlaneReconciler) getStorageConnection(ctx context.Context) (sql.DBConnection, error) {
|
||||
func (r *TenantControlPlaneReconciler) getStorageConnection(ctx context.Context, ds kamajiv1alpha1.DataStore) (sql.DBConnection, error) {
|
||||
var driver sql.Driver
|
||||
var dbName string
|
||||
|
||||
// TODO: https://github.com/clastix/kamaji/issues/67
|
||||
switch r.Config.ETCDStorageType {
|
||||
case types.ETCD:
|
||||
switch ds.Spec.Driver {
|
||||
case kamajiv1alpha1.EtcdDriver:
|
||||
return nil, nil
|
||||
case types.KineMySQL:
|
||||
case kamajiv1alpha1.KineMySQLDriver:
|
||||
driver = sql.MySQL
|
||||
dbName = "mysql"
|
||||
case types.KinePostgreSQL:
|
||||
case kamajiv1alpha1.KinePostgreSQLDriver:
|
||||
driver = sql.PostgreSQL
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
secret := &corev1.Secret{}
|
||||
namespacedName := k8stypes.NamespacedName{Namespace: r.Config.KineSecretNamespace, Name: r.Config.KineSecretName}
|
||||
if err := r.Client.Get(ctx, namespacedName, secret); err != nil {
|
||||
ca, err := ds.Spec.TLSConfig.CertificateAuthority.Certificate.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if t := "kamaji.clastix.io/kine"; string(secret.Type) != t {
|
||||
return nil, fmt.Errorf("expecting a secret of type %s", t)
|
||||
crt, err := ds.Spec.TLSConfig.ClientCertificate.Certificate.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keys := []string{"ca.crt", "server.crt", "server.key", "username", "password"}
|
||||
|
||||
if secret.Data == nil {
|
||||
return nil, fmt.Errorf("the Kine secret %s/%s is missing all the required keys (%s)", secret.GetNamespace(), secret.GetName(), strings.Join(keys, ","))
|
||||
}
|
||||
|
||||
for _, key := range keys {
|
||||
if _, ok := secret.Data[key]; !ok {
|
||||
return nil, fmt.Errorf("missing required key %s for the Kine secret %s/%s", key, secret.GetNamespace(), secret.GetName())
|
||||
}
|
||||
key, err := ds.Spec.TLSConfig.ClientCertificate.PrivateKey.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rootCAs := x509.NewCertPool()
|
||||
if ok := rootCAs.AppendCertsFromPEM(secret.Data["ca.crt"]); !ok {
|
||||
if ok := rootCAs.AppendCertsFromPEM(ca); !ok {
|
||||
return nil, fmt.Errorf("error create root CA for the DB connector")
|
||||
}
|
||||
|
||||
certificate, err := tls.X509KeyPair(secret.Data["server.crt"], secret.Data["server.key"])
|
||||
certificate, err := tls.X509KeyPair(crt, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot retrieve x.509 key pair from the Kine Secret")
|
||||
}
|
||||
|
||||
var user, password string
|
||||
if auth := ds.Spec.BasicAuth; auth != nil {
|
||||
u, err := auth.Username.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user = string(u)
|
||||
|
||||
p, err := auth.Password.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
password = string(p)
|
||||
}
|
||||
|
||||
host, stringPort, err := net.SplitHostPort(ds.Spec.Endpoints[0])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot retrieve host-port pair from DataStore endpoints")
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(stringPort)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot convert port from string for the given DataStore")
|
||||
}
|
||||
|
||||
return sql.GetDBConnection(
|
||||
sql.ConnectionConfig{
|
||||
SQLDriver: driver,
|
||||
User: string(secret.Data["username"]),
|
||||
Password: string(secret.Data["password"]),
|
||||
Host: r.Config.KineHost,
|
||||
Port: r.Config.KinePort,
|
||||
User: user,
|
||||
Password: password,
|
||||
Host: host,
|
||||
Port: port,
|
||||
DBName: dbName,
|
||||
TLSConfig: &tls.Config{
|
||||
ServerName: r.Config.KineHost,
|
||||
ServerName: host,
|
||||
RootCAs: rootCAs,
|
||||
Certificates: []tls.Certificate{certificate},
|
||||
},
|
||||
|
||||
8
controllers/tcp_channel.go
Normal file
8
controllers/tcp_channel.go
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright 2022 Clastix Labs
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package controllers
|
||||
|
||||
import "sigs.k8s.io/controller-runtime/pkg/event"
|
||||
|
||||
type TenantControlPlaneChannel chan event.GenericEvent
|
||||
@@ -7,22 +7,25 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/source"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
kamajierrors "github.com/clastix/kamaji/internal/errors"
|
||||
"github.com/clastix/kamaji/internal/resources"
|
||||
"github.com/clastix/kamaji/internal/sql"
|
||||
"github.com/clastix/kamaji/internal/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,26 +35,16 @@ const (
|
||||
// TenantControlPlaneReconciler reconciles a TenantControlPlane object.
|
||||
type TenantControlPlaneReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
Config TenantControlPlaneReconcilerConfig
|
||||
Scheme *runtime.Scheme
|
||||
Config TenantControlPlaneReconcilerConfig
|
||||
TriggerChan TenantControlPlaneChannel
|
||||
}
|
||||
|
||||
// TenantControlPlaneReconcilerConfig gives the necessary configuration for TenantControlPlaneReconciler.
|
||||
type TenantControlPlaneReconcilerConfig struct {
|
||||
ETCDStorageType types.ETCDStorageType
|
||||
ETCDCASecretName string
|
||||
ETCDCASecretNamespace string
|
||||
ETCDClientSecretName string
|
||||
ETCDClientSecretNamespace string
|
||||
ETCDEndpoints string
|
||||
ETCDCompactionInterval string
|
||||
TmpBaseDirectory string
|
||||
DBConnection sql.DBConnection
|
||||
KineSecretName string
|
||||
KineSecretNamespace string
|
||||
KineHost string
|
||||
KinePort int
|
||||
KineContainerImage string
|
||||
DataStoreName string
|
||||
KineContainerImage string
|
||||
TmpBaseDirectory string
|
||||
}
|
||||
|
||||
//+kubebuilder:rbac:groups=kamaji.clastix.io,resources=tenantcontrolplanes,verbs=get;list;watch;create;update;patch;delete
|
||||
@@ -82,7 +75,12 @@ func (r *TenantControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
dbConnection, err := r.getStorageConnection(ctx)
|
||||
ds := kamajiv1alpha1.DataStore{}
|
||||
if err = r.Client.Get(ctx, k8stypes.NamespacedName{Name: r.Config.DataStoreName}, &ds); err != nil {
|
||||
return ctrl.Result{}, errors.Wrap(err, "cannot retrieve kamajiv1alpha.DataStore object")
|
||||
}
|
||||
|
||||
dbConnection, err := r.getStorageConnection(ctx, ds)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
@@ -104,7 +102,7 @@ func (r *TenantControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R
|
||||
tenantControlPlane: *tenantControlPlane,
|
||||
DBConnection: dbConnection,
|
||||
}
|
||||
registeredDeletableResources := GetDeletableResources(groupDeleteableResourceBuilderConfiguration)
|
||||
registeredDeletableResources := GetDeletableResources(groupDeleteableResourceBuilderConfiguration, ds)
|
||||
|
||||
for _, resource := range registeredDeletableResources {
|
||||
if err := resources.HandleDeletion(ctx, resource, tenantControlPlane); err != nil {
|
||||
@@ -134,9 +132,10 @@ func (r *TenantControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R
|
||||
log: log,
|
||||
tcpReconcilerConfig: r.Config,
|
||||
tenantControlPlane: *tenantControlPlane,
|
||||
DataStore: ds,
|
||||
DBConnection: dbConnection,
|
||||
}
|
||||
registeredResources := GetResources(groupResourceBuilderConfiguration)
|
||||
registeredResources := GetResources(groupResourceBuilderConfiguration, ds)
|
||||
|
||||
for _, resource := range registeredResources {
|
||||
result, err := resources.Handle(ctx, resource, tenantControlPlane)
|
||||
@@ -171,6 +170,14 @@ func (r *TenantControlPlaneReconciler) Reconcile(ctx context.Context, req ctrl.R
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *TenantControlPlaneReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Watches(&source.Channel{Source: r.TriggerChan}, handler.Funcs{GenericFunc: func(genericEvent event.GenericEvent, limitingInterface workqueue.RateLimitingInterface) {
|
||||
limitingInterface.AddRateLimited(ctrl.Request{
|
||||
NamespacedName: k8stypes.NamespacedName{
|
||||
Namespace: genericEvent.Object.GetNamespace(),
|
||||
Name: genericEvent.Object.GetName(),
|
||||
},
|
||||
})
|
||||
}}).
|
||||
For(&kamajiv1alpha1.TenantControlPlane{}).
|
||||
Owns(&corev1.Secret{}).
|
||||
Owns(&corev1.ConfigMap{}).
|
||||
|
||||
@@ -7,41 +7,20 @@ This can help in overcoming the `etcd` limitation regarding scalability and clus
|
||||
|
||||
## Kamaji additional CLI flags
|
||||
|
||||
Once a compatible database is running, we need to provide information about it to Kamaji by using the following flags:
|
||||
Kamaji read the data store configuration from a cluster-scoped resource named `DataStore`, containing all tha required details to secure a connection using a specific driver.
|
||||
|
||||
- [Example of a `etcd` DataStore](./../../config/samples/kamaji_v1alpha1_datastore_etcd.yaml)
|
||||
- [Example of a `MySQL` DataStore](./../../config/samples/kamaji_v1alpha1_datastore_mysql.yaml)
|
||||
- [Example of a `PostgreSQL` DataStore](./../../config/samples/kamaji_v1alpha1_datastore_postgresql.yaml)
|
||||
|
||||
Once the datastore is running, and the `DataStore` has been created with the required details, we need to provide information about it to Kamaji by using the following flag and pointing to the resource name:
|
||||
|
||||
```
|
||||
--etcd-storage-type={kine-mysql,kine-postgresql}
|
||||
--kine-host=<database host>
|
||||
--kine-port=<database port>
|
||||
--kine-secret-name=<secret name>
|
||||
--kine-secret-namespace=<secret namespace>
|
||||
--datastore={.metadata.name}
|
||||
```
|
||||
|
||||
## Kine Secret
|
||||
|
||||
The Kine Secret must be configured as follows:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
data:
|
||||
ca.crt: "content of the Certificate Authority for SSL connection"
|
||||
password: "password of the super user"
|
||||
server.crt: "content of the certificate for SSL connection"
|
||||
server.key: "content of the private key for SSL connection"
|
||||
username: "username of the super user"
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: kine-secret
|
||||
namespace: kamaji-system
|
||||
type: kamaji.clastix.io/kine
|
||||
```
|
||||
|
||||
> Please, pay attention to the type `kamaji.clastix.io/kine`: this check is enforced at the code level to ensure the required data is provided.
|
||||
|
||||
> Actually, the `kine` integration expects a secured connection to the database since the sensitivity data of the Tenant.
|
||||
|
||||
## Drivers
|
||||
|
||||
Further details on the setup for each driver are available here:
|
||||
- [MySQL/MariaDB](../deploy/kine/mysql/README.md)
|
||||
- [PostgreSQL](../deploy/kine/postgresql/README.md)
|
||||
- [PostgreSQL](../deploy/kine/postgresql/README.md)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
|
||||
mariadb: mariadb-certificates mariadb-secret mariadb-kine-secret mariadb-deployment
|
||||
mariadb: mariadb-certificates mariadb-secret mariadb-deployment
|
||||
|
||||
mariadb-certificates:
|
||||
rm -rf $(ROOT_DIR)/certs && mkdir $(ROOT_DIR)/certs
|
||||
@@ -22,15 +22,6 @@ mariadb-secret:
|
||||
--from-literal=MYSQL_ROOT_PASSWORD=root \
|
||||
--dry-run=client -o yaml | kubectl apply -f -
|
||||
|
||||
mariadb-kine-secret:
|
||||
@\
|
||||
CA=$$(cat $(ROOT_DIR)/certs/ca.crt | base64 | tr -d '\n') \
|
||||
CRT=$$(cat $(ROOT_DIR)/certs/server.crt | base64 | tr -d '\n') \
|
||||
KEY=$$(cat $(ROOT_DIR)/certs/server.key | base64 | tr -d '\n') \
|
||||
ROOT_USERNAME=$$(echo -n root | base64) \
|
||||
ROOT_PASSWORD=$$(kubectl -n kamaji-system get secret mysql-config -o jsonpath='{.data.MYSQL_ROOT_PASSWORD}') \
|
||||
envsubst < $(ROOT_DIR)/../secret.yaml | kubectl -n kamaji-system apply -f -
|
||||
|
||||
mariadb-deployment:
|
||||
@kubectl -n kamaji-system apply -f $(ROOT_DIR)/mariadb.yaml
|
||||
|
||||
|
||||
@@ -59,14 +59,6 @@ $ make mariadb-secrets
|
||||
Previous certificates and MySQL configuration have to be available in order to be used.
|
||||
They will be under the secret `kamaji-system:mysql-config`, used by the MySQL/MariaDB instance.
|
||||
|
||||
## Kine Secret
|
||||
|
||||
```bash
|
||||
$ make mariadb-kine-secret
|
||||
```
|
||||
|
||||
Organize the required Kine data such as username, password, CA, certificate, and private key to be stored in the Kamaji desired format.
|
||||
|
||||
## Deployment
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||
|
||||
postgresql: cnpg-setup cnpg-deploy postgresql-secret postgresql-kine-secret
|
||||
postgresql: cnpg-setup cnpg-deploy postgresql-secret
|
||||
|
||||
cnpg-setup:
|
||||
@kubectl apply -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.16.0.yaml
|
||||
@@ -21,15 +21,6 @@ postgresql-secret: cnpg
|
||||
--cnpg-cluster postgresql \
|
||||
--cnpg-user $$(kubectl -n kamaji-system get secret postgresql-superuser -o jsonpath='{.data.username}' | base64 -d)
|
||||
|
||||
postgresql-kine-secret:
|
||||
@\
|
||||
CA=$$(kubectl -n kamaji-system get secret postgresql-ca -o jsonpath='{.data.ca\.crt}') \
|
||||
CRT=$$(kubectl -n kamaji-system get secret postgres-root-cert -o jsonpath='{.data.tls\.crt}') \
|
||||
KEY=$$(kubectl -n kamaji-system get secret postgres-root-cert -o jsonpath='{.data.tls\.key}') \
|
||||
ROOT_USERNAME=$$(kubectl -n kamaji-system get secret postgresql-superuser -o jsonpath='{.data.username}' ) \
|
||||
ROOT_PASSWORD=$$(kubectl -n kamaji-system get secret postgresql-superuser -o jsonpath='{.data.password}' ) \
|
||||
envsubst < $(ROOT_DIR)/../secret.yaml | kubectl -n kamaji-system apply -f -
|
||||
|
||||
postgresql-destroy:
|
||||
@kubectl delete -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/releases/cnpg-1.16.0.yaml --ignore-not-found && \
|
||||
kubectl delete secret postgres-root-cert --ignore-not-found && \
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# PostgreSQL as Kubernetes Storage
|
||||
|
||||
Kamaji offers the possibility of having a different storage system than `etcd` thanks to [kine](https://github.com/k3s-io/kine). One of the implementations is [PostgreSQL](https://www.postgresql.org/).
|
||||
Kamaji offers the possibility of having a different storage system than `etcd` thanks to [kine](https://github.com/k3s-io/kine).
|
||||
One of the implementations is [PostgreSQL](https://www.postgresql.org/).
|
||||
|
||||
Kamaji project is developed using [kind](https://kind.sigs.k8s.io), therefore, a PostgreSQL instance must be deployed in advance into the local kubernetes cluster in order to be used as storage for the tenants.
|
||||
For the sake of simplicity, the [cloudnative-pg](https://cloudnative-pg.io/) Operator will be used to simplify the setup of it.
|
||||
@@ -34,7 +35,6 @@ validatingwebhookconfiguration.admissionregistration.k8s.io/cnpg-validating-webh
|
||||
deployment "cnpg-controller-manager" successfully rolled out
|
||||
cluster.postgresql.cnpg.io/postgresql unchanged
|
||||
secret/postgres-root-cert created
|
||||
secret/kine-secret created
|
||||
```
|
||||
|
||||
## Operator setup
|
||||
@@ -55,15 +55,13 @@ $ make postgresql-secret
|
||||
|
||||
This target will download locally the `kubectl-cnpg` utility to generate an SSL certificate required to secure the connection to the PostgreSQL instance.
|
||||
|
||||
## Kine Secret generation
|
||||
## Certificate generation
|
||||
|
||||
```bash
|
||||
$ make postgresql-kine-secret
|
||||
$ make postgresql-secret
|
||||
```
|
||||
|
||||
Generate the Kine secret required for Kamaji.
|
||||
|
||||
> Requires the generation of the `postgresql-secret`
|
||||
Generate the Certificate required to connect to the DataStore.
|
||||
|
||||
## Teardown
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# secret.yaml is the Secret object that Kamaji is expecting to user to connect to the Kine SQL datastore:
|
||||
# certificates keys are required, username and password are optional.
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
data:
|
||||
ca.crt: ${CA}
|
||||
server.crt: ${CRT}
|
||||
server.key: ${KEY}
|
||||
username: ${ROOT_USERNAME}
|
||||
password: ${ROOT_PASSWORD}
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
name: kine-secret
|
||||
type: kamaji.clastix.io/kine
|
||||
@@ -57,9 +57,7 @@ Kamaji offers the possibility of using a different storage system than `ETCD` fo
|
||||
|
||||
Read it more in the provided [guide](../deploy/kine/README.md).
|
||||
|
||||
|
||||
|
||||
Assuming you adjusted the [Kamaji manifest](./config/install.yaml) to connect to Kine and compatible database using the proper driver, you can now install it.
|
||||
Assuming you adjusted the [Kamaji manifest](../config/install.yaml) to connect to Kine and compatible database using the proper driver, you can now install it.
|
||||
|
||||
### Install Kamaji
|
||||
|
||||
|
||||
@@ -15,49 +15,30 @@ This is easily explained in this way:
|
||||
Available flags are the following:
|
||||
|
||||
```
|
||||
--config-file string Configuration file alternative. (default "./kamaji.yaml")
|
||||
--etcd-ca-secret-name Name of the secret which contains CA's certificate and private key. (default: "etcd-certs")
|
||||
--etcd-ca-secret-namespace Namespace of the secret which contains CA's certificate and private key. (default: "kamaji")
|
||||
--etcd-client-secret-name Name of the secret which contains ETCD client certificates. (default: "root-client-certs")
|
||||
--etcd-client-secret-namespace Name of the namespace where the secret which contains ETCD client certificates is. (default: "kamaji")
|
||||
--etcd-compaction-interval ETCD Compaction interval (i.e. "5m0s"). (default: "0" (disabled))
|
||||
--etcd-endpoints Comma-separated list with ETCD endpoints (i.e. https://etcd-0.etcd.kamaji.svc.cluster.local,https://etcd-1.etcd.kamaji.svc.cluster.local,https://etcd-2.etcd.kamaji.svc.cluster.local)
|
||||
--etcd-storage-type ETCD Storage type (i.e. "etcd", "kine-mysql", "kine-postgresql"). (default: "etcd")
|
||||
--config-file string Configuration file alternative. (default "kamaji.yaml")
|
||||
--datastore string The default DataStore that should be used by Kamaji to setup the required storage (default "etcd")
|
||||
--health-probe-bind-address string The address the probe endpoint binds to. (default ":8081")
|
||||
--kine-port int Port where the DB used by Kine is listening to.
|
||||
--kine-host string Host where the DB used by Kine is working.
|
||||
--kine-secret-name Name of the secret which contains the Kine configuration. (default: "kine-secret")
|
||||
--kine-secret-name Name of the namespace where the secret which contains the Kine configuration. (default: "kamaji-system")
|
||||
--kine-image string 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) (default "rancher/kine:v0.9.2-amd64")
|
||||
--kubeconfig string Paths to a kubeconfig. Only required if out-of-cluster.
|
||||
--leader-elect Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager.
|
||||
--metrics-bind-address string The address the metric endpoint binds to. (default ":8080")
|
||||
--tmp-directory Directory which will be used to work with temporary files. (default "/tmp/kamaji")
|
||||
--tmp-directory string Directory which will be used to work with temporary files. (default "/tmp/kamaji")
|
||||
--zap-devel Development Mode defaults(encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). Production Mode defaults(encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error) (default true)
|
||||
--zap-encoder encoder Zap log encoding (one of 'json' or 'console')
|
||||
--zap-log-level level Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', or any integer value > 0 which corresponds to custom debug levels of increasing verbosity
|
||||
--zap-stacktrace-level level Zap Level at and above which stacktraces are captured (one of 'info', 'error', 'panic').
|
||||
--zap-time-encoding time-encoding Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'). Defaults to 'epoch'.
|
||||
```
|
||||
|
||||
Available environment variables are:
|
||||
|
||||
| Environment variable | Description |
|
||||
| ---------------------------------- | ------------------------------------------------------------ |
|
||||
| `KAMAJI_ETCD_CA_SECRET_NAME` | Name of the secret which contains CA's certificate and private key. (default: "etcd-certs") |
|
||||
| `KAMAJI_ETCD_CA_SECRET_NAMESPACE` | Namespace of the secret which contains CA's certificate and private key. (default: "kamaji") |
|
||||
| `KAMAJI_ETCD_CLIENT_SECRET_NAME` | Name of the secret which contains ETCD client certificates. (default: "root-client-certs") |
|
||||
| `KAMAJI_ETCD_CLIENT_SECRET_NAMESPACE` | Name of the namespace where the secret which contains ETCD client certificates is. (default: "kamaji") |
|
||||
| `KAMAJI_ETCD_COMPACTION_INTERVAL` | ETCD Compaction interval (i.e. "5m0s"). (default: "0" (disabled)) |
|
||||
| `KAMAJI_ETCD_ENDPOINTS` | Comma-separated list with ETCD endpoints (i.e. etcd-server-1:2379,etcd-server-2:2379). (default: "etcd-server:2379") |
|
||||
| `KAMAJI_ETCD_STORAGE_TYPE` | ETCD Storage type (i.e. "etcd", "kine-mysql"). (default: "etcd") |
|
||||
| `KAMAJI_ETCD_SERVERS` | Comma-separated list with ETCD servers (i.e. etcd-0.etcd.kamaji.svc.cluster.local,etcd-1.etcd.kamaji.svc.cluster.local,etcd-2.etcd.kamaji.svc.cluster.local) |
|
||||
| `KAMAJI_METRICS_BIND_ADDRESS` | The address the metric endpoint binds to. (default ":8080") |
|
||||
| `KAMAJI_HEALTH_PROBE_BIND_ADDRESS` | The address the probe endpoint binds to. (default ":8081") |
|
||||
| `KAMAJI_KINE_MYSQL_HOST` | Host where MySQL is running(default "localhost") |
|
||||
| `KAMAJI_KINE_MYSQL_PORT` | Port where MySQL is running (default: 3306) |
|
||||
| `KAMAJI_KINE_MYSQL_SECRET_NAME` | Name of the secret where the necessary configuration and certificates are. (default: "mysql-config") |
|
||||
| `KAMAJI_KINE_MYSQL_SECRET_NAMESPACE` | Name of the namespace of the secret where the necessary configuration and certificates are. (default: "kamaji-system") |
|
||||
| `KAMAJI_LEADER_ELECTION` | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. |
|
||||
| `KAMAJI_TMP_DIRECTORY` | Directory which will be used to work with temporary files. (default "/tmp/kamaji") |
|
||||
| Environment variable | Description |
|
||||
|--------------------------------------|------------------------------------------------------------------------------------------------------------------------|
|
||||
| `KAMAJI_DATASTORE` | Name of the DataStore resource with driver definition and settings. (default "etcd") |
|
||||
| `KAMAJI_METRICS_BIND_ADDRESS` | The address the metric endpoint binds to. (default ":8080") |
|
||||
| `KAMAJI_HEALTH_PROBE_BIND_ADDRESS` | The address the probe endpoint binds to. (default ":8081") |
|
||||
| `KAMAJI_LEADER_ELECTION` | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. |
|
||||
| `KAMAJI_TMP_DIRECTORY` | Directory which will be used to work with temporary files. (default "/tmp/kamaji") |
|
||||
|
||||
|
||||
## Build and deploy
|
||||
@@ -130,3 +111,4 @@ addons:
|
||||
memory: 128Mi
|
||||
serverImage: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-server
|
||||
agentImage: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent
|
||||
```
|
||||
|
||||
@@ -18,7 +18,6 @@ import (
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
"github.com/clastix/kamaji/internal/types"
|
||||
"github.com/clastix/kamaji/internal/utilities"
|
||||
)
|
||||
|
||||
@@ -28,7 +27,6 @@ const (
|
||||
apiServerIndex orderedIndex = iota
|
||||
schedulerIndex
|
||||
controllerManagerIndex
|
||||
kineIndex
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -39,13 +37,12 @@ const (
|
||||
usrLocalShareCACertificates
|
||||
schedulerKubeconfig
|
||||
controllerManagerKubeconfig
|
||||
kineConfig
|
||||
kineCerts
|
||||
)
|
||||
|
||||
const (
|
||||
apiServerFlagsAnnotation = "kube-apiserver.kamaji.clastix.io/args"
|
||||
kineVolumeName = "kine-config"
|
||||
kineContainerName = "kine"
|
||||
kineVolumeChmod = "kine-config"
|
||||
kineVolumeCertName = "kine-certs"
|
||||
)
|
||||
|
||||
@@ -53,7 +50,7 @@ type Deployment struct {
|
||||
Address string
|
||||
ETCDEndpoints []string
|
||||
ETCDCompactionInterval string
|
||||
ETCDStorageType types.ETCDStorageType
|
||||
ETCDStorageType kamajiv1alpha1.Driver
|
||||
KineContainerImage string
|
||||
}
|
||||
|
||||
@@ -119,7 +116,7 @@ func (d *Deployment) buildPKIVolume(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1
|
||||
},
|
||||
}
|
||||
|
||||
if d.ETCDStorageType == types.ETCD {
|
||||
if d.ETCDStorageType == kamajiv1alpha1.EtcdDriver {
|
||||
sources = append(sources, corev1.VolumeProjection{
|
||||
Secret: d.secretProjection(tcp.Status.Certificates.ETCD.APIServer.SecretName, constants.APIServerEtcdClientCertName, constants.APIServerEtcdClientKeyName),
|
||||
})
|
||||
@@ -552,8 +549,7 @@ func (d *Deployment) buildKubeAPIServerCommand(tenantControlPlane *kamajiv1alpha
|
||||
"--tls-private-key-file": path.Join(v1beta3.DefaultCertificatesDir, constants.APIServerKeyName),
|
||||
}
|
||||
|
||||
if d.ETCDStorageType == types.ETCD {
|
||||
desiredArgs["--etcd-compaction-interval"] = d.ETCDCompactionInterval
|
||||
if d.ETCDStorageType == kamajiv1alpha1.EtcdDriver {
|
||||
desiredArgs["--etcd-cafile"] = path.Join(v1beta3.DefaultCertificatesDir, constants.EtcdCACertName)
|
||||
desiredArgs["--etcd-certfile"] = path.Join(v1beta3.DefaultCertificatesDir, constants.APIServerEtcdClientCertName)
|
||||
desiredArgs["--etcd-keyfile"] = path.Join(v1beta3.DefaultCertificatesDir, constants.APIServerEtcdClientKeyName)
|
||||
@@ -582,10 +578,8 @@ func (d *Deployment) secretProjection(secretName, certKeyName, keyName string) *
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Deployment) buildKineVolume(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.TenantControlPlane) {
|
||||
// Kine is expecting an additional volume for its configuration, and it must be removed before proceeding with the
|
||||
// customized storage that is idempotent
|
||||
if found, index := utilities.HasNamedVolume(podSpec.Volumes, kineVolumeName); found {
|
||||
func (d *Deployment) removeKineVolumes(podSpec *corev1.PodSpec) {
|
||||
if found, index := utilities.HasNamedVolume(podSpec.Volumes, kineVolumeChmod); found {
|
||||
var volumes []corev1.Volume
|
||||
|
||||
volumes = append(volumes, podSpec.Volumes[:index]...)
|
||||
@@ -602,37 +596,44 @@ func (d *Deployment) buildKineVolume(podSpec *corev1.PodSpec, tcp *kamajiv1alpha
|
||||
|
||||
podSpec.Volumes = volumes
|
||||
}
|
||||
}
|
||||
|
||||
if d.ETCDStorageType == types.KineMySQL || d.ETCDStorageType == types.KinePostgreSQL {
|
||||
if index := int(kineConfig) + 1; len(podSpec.Volumes) < index {
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
}
|
||||
// Adding the volume to read Kine certificates:
|
||||
// these must be subsequently fixed with a chmod due to pg issues with private key.
|
||||
podSpec.Volumes[kineConfig].Name = kineVolumeName
|
||||
podSpec.Volumes[kineConfig].VolumeSource = corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: tcp.Status.Storage.Kine.Certificate.SecretName,
|
||||
DefaultMode: pointer.Int32Ptr(420),
|
||||
},
|
||||
}
|
||||
// Adding the Volume for the certificates with fixed permission
|
||||
if index := int(kineCerts) + 1; len(podSpec.Volumes) < index {
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
}
|
||||
func (d *Deployment) buildKineVolume(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.TenantControlPlane) {
|
||||
if d.ETCDStorageType == kamajiv1alpha1.EtcdDriver {
|
||||
d.removeKineVolumes(podSpec)
|
||||
|
||||
podSpec.Volumes[kineCerts].Name = kineVolumeCertName
|
||||
podSpec.Volumes[kineCerts].VolumeSource = corev1.VolumeSource{
|
||||
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||
}
|
||||
return
|
||||
}
|
||||
// Adding the volume for chmod'ed Kine certificates.
|
||||
found, index := utilities.HasNamedVolume(podSpec.Volumes, kineVolumeChmod)
|
||||
if !found {
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
index = len(podSpec.Volumes) - 1
|
||||
}
|
||||
|
||||
podSpec.Volumes[index].Name = kineVolumeChmod
|
||||
podSpec.Volumes[index].VolumeSource = corev1.VolumeSource{
|
||||
Secret: &corev1.SecretVolumeSource{
|
||||
SecretName: tcp.Status.Storage.Kine.Certificate.SecretName,
|
||||
DefaultMode: pointer.Int32Ptr(420),
|
||||
},
|
||||
}
|
||||
// Adding the volume to read Kine certificates:
|
||||
// these must be subsequently fixed with a chmod due to pg issues with private key.
|
||||
if found, index = utilities.HasNamedVolume(podSpec.Volumes, kineVolumeCertName); !found {
|
||||
podSpec.Volumes = append(podSpec.Volumes, corev1.Volume{})
|
||||
index = len(podSpec.Volumes) - 1
|
||||
}
|
||||
|
||||
podSpec.Volumes[index].Name = kineVolumeCertName
|
||||
podSpec.Volumes[index].VolumeSource = corev1.VolumeSource{
|
||||
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.TenantControlPlane) {
|
||||
const kineContainerName = "kine"
|
||||
// Kine is expecting an additional container, and it must be removed before proceeding with the additional one
|
||||
// in order to make this function idempotent.
|
||||
if found, index := utilities.HasNamedContainer(podSpec.Containers, kineContainerName); found {
|
||||
func (d *Deployment) removeKineContainers(podSpec *corev1.PodSpec) {
|
||||
found, index := utilities.HasNamedContainer(podSpec.Containers, kineContainerName)
|
||||
if found {
|
||||
var containers []corev1.Container
|
||||
|
||||
containers = append(containers, podSpec.Containers[:index]...)
|
||||
@@ -640,13 +641,22 @@ func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.Tena
|
||||
|
||||
podSpec.Containers = containers
|
||||
}
|
||||
// In case of bare ETCD we exit without mangling the PodSpec resource.
|
||||
if d.ETCDStorageType == types.ETCD {
|
||||
|
||||
podSpec.InitContainers = nil
|
||||
}
|
||||
|
||||
func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.TenantControlPlane) {
|
||||
if d.ETCDStorageType == kamajiv1alpha1.EtcdDriver {
|
||||
d.removeKineContainers(podSpec)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if index := int(kineIndex) + 1; len(podSpec.Containers) < index {
|
||||
// Kine is expecting an additional container, and it must be removed before proceeding with the additional one
|
||||
// in order to make this function idempotent.
|
||||
found, index := utilities.HasNamedContainer(podSpec.Containers, kineContainerName)
|
||||
if !found {
|
||||
podSpec.Containers = append(podSpec.Containers, corev1.Container{})
|
||||
index = len(podSpec.Containers) - 1
|
||||
}
|
||||
|
||||
args := map[string]string{}
|
||||
@@ -656,9 +666,9 @@ func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.Tena
|
||||
}
|
||||
|
||||
switch d.ETCDStorageType {
|
||||
case types.KineMySQL:
|
||||
case kamajiv1alpha1.KineMySQLDriver:
|
||||
args["--endpoint"] = "mysql://$(DB_USER):$(DB_PASSWORD)@tcp($(DB_HOST):$(DB_PORT))/$(DB_SCHEMA)"
|
||||
case types.KinePostgreSQL:
|
||||
case kamajiv1alpha1.KinePostgreSQLDriver:
|
||||
args["--endpoint"] = "postgres://$(DB_USER):$(DB_PASSWORD)@$(DB_HOST):$(DB_PORT)/$(DB_SCHEMA)"
|
||||
}
|
||||
|
||||
@@ -680,7 +690,7 @@ func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.Tena
|
||||
},
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
Name: kineVolumeName,
|
||||
Name: kineVolumeChmod,
|
||||
ReadOnly: true,
|
||||
MountPath: "/kine",
|
||||
},
|
||||
@@ -693,26 +703,26 @@ func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.Tena
|
||||
},
|
||||
}
|
||||
|
||||
podSpec.Containers[kineIndex].Name = kineContainerName
|
||||
podSpec.Containers[kineIndex].Image = d.KineContainerImage
|
||||
podSpec.Containers[kineIndex].Command = []string{"/bin/kine"}
|
||||
podSpec.Containers[kineIndex].Args = utilities.ArgsFromMapToSlice(args)
|
||||
podSpec.Containers[kineIndex].VolumeMounts = []corev1.VolumeMount{
|
||||
podSpec.Containers[index].Name = kineContainerName
|
||||
podSpec.Containers[index].Image = d.KineContainerImage
|
||||
podSpec.Containers[index].Command = []string{"/bin/kine"}
|
||||
podSpec.Containers[index].Args = utilities.ArgsFromMapToSlice(args)
|
||||
podSpec.Containers[index].VolumeMounts = []corev1.VolumeMount{
|
||||
{
|
||||
Name: kineVolumeCertName,
|
||||
MountPath: "/certs",
|
||||
ReadOnly: false,
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].TerminationMessagePath = corev1.TerminationMessagePathDefault
|
||||
podSpec.Containers[kineIndex].TerminationMessagePolicy = corev1.TerminationMessageReadFile
|
||||
podSpec.Containers[kineIndex].Env = []corev1.EnvVar{
|
||||
podSpec.Containers[index].TerminationMessagePath = corev1.TerminationMessagePathDefault
|
||||
podSpec.Containers[index].TerminationMessagePolicy = corev1.TerminationMessageReadFile
|
||||
podSpec.Containers[index].Env = []corev1.EnvVar{
|
||||
{
|
||||
Name: "GODEBUG",
|
||||
Value: "x509ignoreCN=0",
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].EnvFrom = []corev1.EnvFromSource{
|
||||
podSpec.Containers[index].EnvFrom = []corev1.EnvFromSource{
|
||||
{
|
||||
SecretRef: &corev1.SecretEnvSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
@@ -721,14 +731,14 @@ func (d *Deployment) buildKine(podSpec *corev1.PodSpec, tcp *kamajiv1alpha1.Tena
|
||||
},
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].Ports = []corev1.ContainerPort{
|
||||
podSpec.Containers[index].Ports = []corev1.ContainerPort{
|
||||
{
|
||||
ContainerPort: 2379,
|
||||
Name: "server",
|
||||
Protocol: corev1.ProtocolTCP,
|
||||
},
|
||||
}
|
||||
podSpec.Containers[kineIndex].ImagePullPolicy = corev1.PullAlways
|
||||
podSpec.Containers[index].ImagePullPolicy = corev1.PullAlways
|
||||
}
|
||||
|
||||
func (d *Deployment) SetSelector(deploymentSpec *appsv1.DeploymentSpec, tcp *kamajiv1alpha1.TenantControlPlane) {
|
||||
|
||||
@@ -21,18 +21,10 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
envPrefix = "KAMAJI"
|
||||
defaultETCDStorageType = "etcd"
|
||||
defaultETCDCASecretName = "etcd-certs"
|
||||
defaultETCDCASecretNamespace = "kamaji-system"
|
||||
defaultETCDEndpoints = "etcd-server:2379"
|
||||
defaultETCDCompactionInterval = "0"
|
||||
defaultETCDClientSecretName = "root-client-certs"
|
||||
defaultETCDClientSecretNamespace = "kamaji-system"
|
||||
defaultTmpDirectory = "/tmp/kamaji"
|
||||
defaultKineSecretName = "kine-secret"
|
||||
defaultKineSecretNamespace = "kamaji-system"
|
||||
defaultKineImage = "rancher/kine:v0.9.2-amd64"
|
||||
envPrefix = "KAMAJI"
|
||||
defaultTmpDirectory = "/tmp/kamaji"
|
||||
defaultKineImage = "rancher/kine:v0.9.2-amd64"
|
||||
defaultDataStore = "etcd"
|
||||
)
|
||||
|
||||
func InitConfig() (*viper.Viper, error) {
|
||||
@@ -44,19 +36,9 @@ func InitConfig() (*viper.Viper, error) {
|
||||
flag.String("health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
flag.Bool("leader-elect", false, "Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.String("etcd-storage-type", defaultETCDStorageType, "Type of storage for ETCD (i.e etcd, kine-mysql, kine-psql)")
|
||||
flag.String("etcd-ca-secret-name", defaultETCDCASecretName, "Name of the secret which contains CA's certificate and private key.")
|
||||
flag.String("etcd-ca-secret-namespace", defaultETCDCASecretNamespace, "Namespace of the secret which contains CA's certificate and private key.")
|
||||
flag.String("etcd-client-secret-name", defaultETCDClientSecretName, "Name of the secret which contains ETCD client certificates")
|
||||
flag.String("etcd-client-secret-namespace", defaultETCDClientSecretNamespace, "Name of the namespace where the secret which contains ETCD client certificates is")
|
||||
flag.String("etcd-endpoints", defaultETCDEndpoints, "Comma-separated list with ETCD endpoints (i.e. https://etcd-0.etcd.kamaji-system.svc.cluster.local,https://etcd-1.etcd.kamaji-system.svc.cluster.local,https://etcd-2.etcd.kamaji-system.svc.cluster.local)")
|
||||
flag.String("etcd-compaction-interval", defaultETCDCompactionInterval, "ETCD Compaction interval (i.e. \"5m0s\"). (default: \"0\" (disabled))")
|
||||
flag.String("tmp-directory", defaultTmpDirectory, "Directory which will be used to work with temporary files.")
|
||||
flag.String("kine-secret-name", defaultKineSecretName, "Name of the secret which contains the Kine configuration.")
|
||||
flag.String("kine-secret-namespace", defaultKineSecretNamespace, "Name of the namespace where the secret which contains the Kine configuration.")
|
||||
flag.String("kine-host", "", "Host where the DB used by Kine is working.")
|
||||
flag.Int("kine-port", 0, "Port where the DB used by Kine is listening to.")
|
||||
flag.String("kine-image", defaultKineImage, "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)")
|
||||
flag.String("datastore", defaultDataStore, "The default DataStore that should be used by Kamaji to setup the required storage")
|
||||
|
||||
// Setup zap configuration
|
||||
opts := zap.Options{
|
||||
@@ -84,45 +66,15 @@ func InitConfig() (*viper.Viper, error) {
|
||||
if err := config.BindEnv("leader-elect", fmt.Sprintf("%s_LEADER_ELECTION", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("etcd-storage-type", fmt.Sprintf("%s_ETCD_STORAGE_TYPE", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("etcd-ca-secret-name", fmt.Sprintf("%s_ETCD_CA_SECRET_NAME", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("etcd-ca-secret-namespace", fmt.Sprintf("%s_ETCD_CA_SECRET_NAMESPACE", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("etcd-client-secret-name", fmt.Sprintf("%s_ETCD_CLIENT_SECRET_NAME", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("etcd-client-secret-namespace", fmt.Sprintf("%s_ETCD_CLIENT_SECRET_NAMESPACE", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("etcd-endpoints", fmt.Sprintf("%s_ETCD_ENDPOINTS", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("etcd-compaction-interval", fmt.Sprintf("%s_ETCD_COMPACTION_INTERVAL", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("tmp-directory", fmt.Sprintf("%s_TMP_DIRECTORY", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-secret-name", fmt.Sprintf("%s_KINE_SECRET_NAME", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-secret-namespace", fmt.Sprintf("%s_KINE_SECRET_NAMESPACE", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-host", fmt.Sprintf("%s_KINE_HOST", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-port", fmt.Sprintf("%s_KINE_PORT", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("kine-image", fmt.Sprintf("%s_KINE_IMAGE", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := config.BindEnv("datastore", fmt.Sprintf("%s_DATASTORE", envPrefix)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Setup config file
|
||||
if cfgFile != "" {
|
||||
|
||||
@@ -90,7 +90,7 @@ func getKubeadmClusterConfiguration(params Parameters) kubeadmapi.ClusterConfigu
|
||||
}, params.TenantControlPlaneCertSANs...),
|
||||
ControlPlaneComponent: kubeadmapi.ControlPlaneComponent{
|
||||
ExtraArgs: map[string]string{
|
||||
"etcd-compaction-interval": params.ETCDCompactionInterval,
|
||||
"etcd-compaction-interval": "0s",
|
||||
"etcd-prefix": fmt.Sprintf("/%s", params.TenantControlPlaneName),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -44,7 +44,6 @@ type Parameters struct {
|
||||
TenantControlPlaneVersion string
|
||||
TenantControlPlaneCGroupDriver string
|
||||
ETCDs []string
|
||||
ETCDCompactionInterval string
|
||||
CertificatesDir string
|
||||
KubeconfigDir string
|
||||
}
|
||||
|
||||
@@ -25,23 +25,22 @@ type APIServerCertificate struct {
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
TmpDirectory string
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *APIServerCertificate) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Certificates.APIServer.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *APIServerCertificate) ShouldCleanup(_ *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *APIServerCertificate) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *APIServerCertificate) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -53,7 +52,7 @@ func (r *APIServerCertificate) Define(ctx context.Context, tenantControlPlane *k
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) GetClient() client.Client {
|
||||
@@ -69,10 +68,10 @@ func (r *APIServerCertificate) CreateOrUpdate(ctx context.Context, tenantControl
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) GetName() string {
|
||||
return r.Name
|
||||
return "api-server-certificate"
|
||||
}
|
||||
|
||||
func (r *APIServerCertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *APIServerCertificate) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
tenantControlPlane.Status.Certificates.APIServer.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Certificates.APIServer.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Certificates.APIServer.Checksum = r.resource.GetAnnotations()["checksum"]
|
||||
|
||||
@@ -25,23 +25,22 @@ type APIServerKubeletClientCertificate struct {
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
TmpDirectory string
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *APIServerKubeletClientCertificate) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Certificates.APIServerKubeletClient.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *APIServerKubeletClientCertificate) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *APIServerKubeletClientCertificate) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *APIServerKubeletClientCertificate) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -53,7 +52,7 @@ func (r *APIServerKubeletClientCertificate) Define(ctx context.Context, tenantCo
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) GetClient() client.Client {
|
||||
@@ -69,10 +68,10 @@ func (r *APIServerKubeletClientCertificate) CreateOrUpdate(ctx context.Context,
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) GetName() string {
|
||||
return r.Name
|
||||
return "api-server-kubelet-client-certificate"
|
||||
}
|
||||
|
||||
func (r *APIServerKubeletClientCertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *APIServerKubeletClientCertificate) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
tenantControlPlane.Status.Certificates.APIServerKubeletClient.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Certificates.APIServerKubeletClient.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Certificates.APIServerKubeletClient.Checksum = r.resource.GetAnnotations()["checksum"]
|
||||
|
||||
@@ -24,24 +24,23 @@ type CACertificate struct {
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
TmpDirectory string
|
||||
}
|
||||
|
||||
func (r *CACertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *CACertificate) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Certificates.CA.SecretName != r.resource.GetName() ||
|
||||
tenantControlPlane.Status.Certificates.CA.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *CACertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *CACertificate) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *CACertificate) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *CACertificate) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *CACertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *CACertificate) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -53,7 +52,7 @@ func (r *CACertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1
|
||||
}
|
||||
|
||||
func (r *CACertificate) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *CACertificate) GetClient() client.Client {
|
||||
@@ -69,10 +68,10 @@ func (r *CACertificate) CreateOrUpdate(ctx context.Context, tenantControlPlane *
|
||||
}
|
||||
|
||||
func (r *CACertificate) GetName() string {
|
||||
return r.Name
|
||||
return "ca"
|
||||
}
|
||||
|
||||
func (r *CACertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *CACertificate) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
tenantControlPlane.Status.Certificates.CA.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Certificates.CA.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Certificates.CA.Checksum = r.resource.GetAnnotations()["checksum"]
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/go-logr/logr"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -22,15 +21,14 @@ import (
|
||||
)
|
||||
|
||||
type ETCDCACertificatesResource struct {
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
ETCDCASecretName string
|
||||
ETCDCASecretNamespace string
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
DataStore kamajiv1alpha1.DataStore
|
||||
}
|
||||
|
||||
func (r *ETCDCACertificatesResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *ETCDCACertificatesResource) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
if tenantControlPlane.Status.Certificates.ETCD == nil {
|
||||
return true
|
||||
}
|
||||
@@ -38,15 +36,15 @@ func (r *ETCDCACertificatesResource) ShouldStatusBeUpdated(ctx context.Context,
|
||||
return tenantControlPlane.Status.Certificates.ETCD.CA.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *ETCDCACertificatesResource) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *ETCDCACertificatesResource) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *ETCDCACertificatesResource) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *ETCDCACertificatesResource) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *ETCDCACertificatesResource) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *ETCDCACertificatesResource) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -65,7 +63,7 @@ func (r *ETCDCACertificatesResource) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func (r *ETCDCACertificatesResource) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *ETCDCACertificatesResource) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Certificates.ETCD == nil {
|
||||
tenantControlPlane.Status.Certificates.ETCD = &kamajiv1alpha1.ETCDCertificatesStatus{}
|
||||
}
|
||||
@@ -83,6 +81,10 @@ func (r *ETCDCACertificatesResource) getPrefixedName(tenantControlPlane *kamajiv
|
||||
|
||||
func (r *ETCDCACertificatesResource) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn {
|
||||
return func() error {
|
||||
if r.DataStore.Spec.TLSConfig.CertificateAuthority.PrivateKey == nil {
|
||||
return fmt.Errorf("missing private key, cannot generate certificate for the given tenant control plane")
|
||||
}
|
||||
|
||||
if etcdStatus := tenantControlPlane.Status.Certificates.ETCD; etcdStatus != nil && len(etcdStatus.CA.Checksum) > 0 && etcdStatus.CA.Checksum == r.resource.GetAnnotations()["checksum"] {
|
||||
isValid, err := etcd.IsETCDCertificateAndKeyPairValid(r.resource.Data[kubeadmconstants.CACertName], r.resource.Data[kubeadmconstants.CAKeyName])
|
||||
if err != nil {
|
||||
@@ -94,15 +96,19 @@ func (r *ETCDCACertificatesResource) mutate(ctx context.Context, tenantControlPl
|
||||
}
|
||||
}
|
||||
|
||||
etcdCASecretNamespacedName := k8stypes.NamespacedName{Namespace: r.ETCDCASecretNamespace, Name: r.ETCDCASecretName}
|
||||
etcdCASecret := &corev1.Secret{}
|
||||
if err := r.Client.Get(ctx, etcdCASecretNamespacedName, etcdCASecret); err != nil {
|
||||
ca, err := r.DataStore.Spec.TLSConfig.CertificateAuthority.Certificate.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := r.DataStore.Spec.TLSConfig.CertificateAuthority.PrivateKey.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.resource.Data = map[string][]byte{
|
||||
kubeadmconstants.CACertName: etcdCASecret.Data[kubeadmconstants.CACertName],
|
||||
kubeadmconstants.CAKeyName: etcdCASecret.Data[kubeadmconstants.CAKeyName],
|
||||
kubeadmconstants.CACertName: ca,
|
||||
kubeadmconstants.CAKeyName: key,
|
||||
}
|
||||
|
||||
r.resource.SetLabels(utilities.KamajiLabels())
|
||||
|
||||
@@ -28,7 +28,7 @@ type ETCDCertificatesResource struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (r *ETCDCertificatesResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *ETCDCertificatesResource) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
if tenantControlPlane.Status.Certificates.ETCD == nil {
|
||||
return true
|
||||
}
|
||||
@@ -36,15 +36,15 @@ func (r *ETCDCertificatesResource) ShouldStatusBeUpdated(ctx context.Context, te
|
||||
return tenantControlPlane.Status.Certificates.ETCD.APIServer.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *ETCDCertificatesResource) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *ETCDCertificatesResource) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *ETCDCertificatesResource) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *ETCDCertificatesResource) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *ETCDCertificatesResource) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *ETCDCertificatesResource) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
|
||||
@@ -8,9 +8,6 @@ import (
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
etcdclient "go.etcd.io/etcd/client/v3"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
@@ -18,26 +15,19 @@ import (
|
||||
"github.com/clastix/kamaji/internal/etcd"
|
||||
)
|
||||
|
||||
const (
|
||||
caKeyName = kubeadmconstants.CACertName
|
||||
)
|
||||
|
||||
type etcdSetupResource struct {
|
||||
role etcd.Role
|
||||
user etcd.User
|
||||
}
|
||||
|
||||
type ETCDSetupResource struct {
|
||||
resource *etcdSetupResource
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
Endpoints []string
|
||||
ETCDClientCertsSecret k8stypes.NamespacedName
|
||||
ETCDCACertsSecret k8stypes.NamespacedName
|
||||
resource *etcdSetupResource
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
DataStore kamajiv1alpha1.DataStore
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *ETCDSetupResource) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
if tenantControlPlane.Status.Storage.ETCD == nil {
|
||||
return true
|
||||
}
|
||||
@@ -46,15 +36,15 @@ func (r *ETCDSetupResource) ShouldStatusBeUpdated(ctx context.Context, tenantCon
|
||||
tenantControlPlane.Status.Storage.ETCD.User.Name != r.resource.user.Name
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *ETCDSetupResource) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *ETCDSetupResource) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *ETCDSetupResource) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &etcdSetupResource{
|
||||
role: etcd.Role{Name: tenantControlPlane.Name, Exists: false},
|
||||
user: etcd.User{Name: tenantControlPlane.Name, Exists: false},
|
||||
@@ -63,7 +53,7 @@ func (r *ETCDSetupResource) Define(ctx context.Context, tenantControlPlane *kama
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
func (r *ETCDSetupResource) CreateOrUpdate(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
return r.reconcile(ctx)
|
||||
}
|
||||
|
||||
@@ -93,10 +83,10 @@ func (r *ETCDSetupResource) Delete(ctx context.Context, tenantControlPlane *kama
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) GetName() string {
|
||||
return r.Name
|
||||
return "etcd-setup"
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *ETCDSetupResource) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Storage.ETCD == nil {
|
||||
tenantControlPlane.Status.Storage.ETCD = &kamajiv1alpha1.ETCDStatus{}
|
||||
}
|
||||
@@ -144,21 +134,26 @@ func (r *ETCDSetupResource) reconcile(ctx context.Context) (controllerutil.Opera
|
||||
}
|
||||
|
||||
func (r *ETCDSetupResource) getETCDClient(ctx context.Context) (*etcdclient.Client, error) {
|
||||
var certsClientSecret corev1.Secret
|
||||
if err := r.Client.Get(ctx, r.ETCDClientCertsSecret, &certsClientSecret); err != nil {
|
||||
ca, err := r.DataStore.Spec.TLSConfig.CertificateAuthority.Certificate.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var certsCASecret corev1.Secret
|
||||
if err := r.Client.Get(ctx, r.ETCDCACertsSecret, &certsCASecret); err != nil {
|
||||
crt, err := r.DataStore.Spec.TLSConfig.ClientCertificate.Certificate.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, err := r.DataStore.Spec.TLSConfig.ClientCertificate.PrivateKey.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
config := etcd.Config{
|
||||
ETCDCertificate: certsClientSecret.Data[corev1.TLSCertKey],
|
||||
ETCDPrivateKey: certsClientSecret.Data[corev1.TLSPrivateKeyKey],
|
||||
ETCDCA: certsCASecret.Data[caKeyName],
|
||||
Endpoints: r.Endpoints,
|
||||
ETCDCertificate: crt,
|
||||
ETCDPrivateKey: key,
|
||||
ETCDCA: ca,
|
||||
Endpoints: r.DataStore.Spec.Endpoints,
|
||||
}
|
||||
|
||||
return etcd.NewClient(config)
|
||||
|
||||
@@ -25,23 +25,22 @@ type FrontProxyClientCertificate struct {
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
TmpDirectory string
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *FrontProxyClientCertificate) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Certificates.FrontProxyClient.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *FrontProxyClientCertificate) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *FrontProxyClientCertificate) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *FrontProxyClientCertificate) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -53,7 +52,7 @@ func (r *FrontProxyClientCertificate) Define(ctx context.Context, tenantControlP
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) GetClient() client.Client {
|
||||
@@ -69,10 +68,10 @@ func (r *FrontProxyClientCertificate) CreateOrUpdate(ctx context.Context, tenant
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) GetName() string {
|
||||
return r.Name
|
||||
return "front-proxy-client-certificate"
|
||||
}
|
||||
|
||||
func (r *FrontProxyClientCertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *FrontProxyClientCertificate) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
tenantControlPlane.Status.Certificates.FrontProxyClient.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Certificates.FrontProxyClient.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Certificates.FrontProxyClient.Checksum = r.resource.GetAnnotations()["checksum"]
|
||||
|
||||
@@ -24,23 +24,22 @@ type FrontProxyCACertificate struct {
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Log logr.Logger
|
||||
Name string
|
||||
TmpDirectory string
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *FrontProxyCACertificate) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Certificates.FrontProxyCA.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *FrontProxyCACertificate) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *FrontProxyCACertificate) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *FrontProxyCACertificate) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -48,13 +47,11 @@ func (r *FrontProxyCACertificate) Define(ctx context.Context, tenantControlPlane
|
||||
},
|
||||
}
|
||||
|
||||
r.Name = "front-proxy-ca-certificate"
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) GetClient() client.Client {
|
||||
@@ -70,10 +67,10 @@ func (r *FrontProxyCACertificate) CreateOrUpdate(ctx context.Context, tenantCont
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) GetName() string {
|
||||
return r.Name
|
||||
return "front-proxy-ca-certificate"
|
||||
}
|
||||
|
||||
func (r *FrontProxyCACertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *FrontProxyCACertificate) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
tenantControlPlane.Status.Certificates.FrontProxyCA.LastUpdate = metav1.Now()
|
||||
tenantControlPlane.Status.Certificates.FrontProxyCA.SecretName = r.resource.GetName()
|
||||
tenantControlPlane.Status.Certificates.FrontProxyCA.Checksum = r.resource.GetAnnotations()["checksum"]
|
||||
|
||||
@@ -14,18 +14,16 @@ import (
|
||||
|
||||
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
|
||||
builder "github.com/clastix/kamaji/internal/builders/controlplane"
|
||||
"github.com/clastix/kamaji/internal/types"
|
||||
"github.com/clastix/kamaji/internal/utilities"
|
||||
)
|
||||
|
||||
type KubernetesDeploymentResource struct {
|
||||
resource *appsv1.Deployment
|
||||
Client client.Client
|
||||
ETCDStorageType types.ETCDStorageType
|
||||
ETCDEndpoints []string
|
||||
ETCDCompactionInterval string
|
||||
Name string
|
||||
KineContainerImage string
|
||||
resource *appsv1.Deployment
|
||||
Client client.Client
|
||||
DataStoreDriver kamajiv1alpha1.Driver
|
||||
ETCDEndpoints []string
|
||||
Name string
|
||||
KineContainerImage string
|
||||
}
|
||||
|
||||
func (r *KubernetesDeploymentResource) isStatusEqual(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
@@ -65,11 +63,10 @@ func (r *KubernetesDeploymentResource) mutate(ctx context.Context, tenantControl
|
||||
}
|
||||
|
||||
d := builder.Deployment{
|
||||
Address: address,
|
||||
ETCDEndpoints: r.ETCDEndpoints,
|
||||
ETCDCompactionInterval: r.ETCDCompactionInterval,
|
||||
ETCDStorageType: r.ETCDStorageType,
|
||||
KineContainerImage: r.KineContainerImage,
|
||||
Address: address,
|
||||
ETCDEndpoints: r.ETCDEndpoints,
|
||||
ETCDStorageType: r.DataStoreDriver,
|
||||
KineContainerImage: r.KineContainerImage,
|
||||
}
|
||||
d.SetLabels(r.resource, utilities.MergeMaps(utilities.CommonLabels(tenantControlPlane.GetName()), tenantControlPlane.Spec.ControlPlane.Deployment.AdditionalMetadata.Labels))
|
||||
d.SetAnnotations(r.resource, utilities.MergeMaps(r.resource.Annotations, tenantControlPlane.Spec.ControlPlane.Deployment.AdditionalMetadata.Annotations))
|
||||
@@ -135,7 +132,7 @@ func (r *KubernetesDeploymentResource) deploymentTemplateLabels(ctx context.Cont
|
||||
"component.kamaji.clastix.io/scheduler-kubeconfig": hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.KubeConfig.Scheduler.SecretName),
|
||||
}
|
||||
|
||||
if r.ETCDStorageType == types.ETCD {
|
||||
if r.DataStoreDriver == kamajiv1alpha1.EtcdDriver {
|
||||
labels["component.kamaji.clastix.io/etcd-ca-certificates"] = hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.Certificates.ETCD.CA.SecretName)
|
||||
labels["component.kamaji.clastix.io/etcd-certificates"] = hash(ctx, tenantControlPlane.GetNamespace(), tenantControlPlane.Status.Certificates.ETCD.APIServer.SecretName)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ type KubernetesIngressResource struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (r *KubernetesIngressResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubernetesIngressResource) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return !(tenantControlPlane.Status.Kubernetes.Ingress.Name == r.resource.GetName() &&
|
||||
tenantControlPlane.Status.Kubernetes.Ingress.Namespace == r.resource.GetNamespace())
|
||||
}
|
||||
@@ -33,7 +33,7 @@ func (r *KubernetesIngressResource) ShouldCleanup(tenantControlPlane *kamajiv1al
|
||||
return tenantControlPlane.Spec.ControlPlane.Ingress == nil
|
||||
}
|
||||
|
||||
func (r *KubernetesIngressResource) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *KubernetesIngressResource) CleanUp(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
if err := r.Client.Delete(ctx, r.resource); err != nil {
|
||||
if !k8serrors.IsNotFound(err) {
|
||||
return false, err
|
||||
@@ -45,7 +45,7 @@ func (r *KubernetesIngressResource) CleanUp(ctx context.Context, tenantControlPl
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *KubernetesIngressResource) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubernetesIngressResource) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Spec.ControlPlane.Ingress != nil {
|
||||
tenantControlPlane.Status.Kubernetes.Ingress.IngressStatus = r.resource.Status
|
||||
tenantControlPlane.Status.Kubernetes.Ingress.Name = r.resource.GetName()
|
||||
@@ -59,7 +59,7 @@ func (r *KubernetesIngressResource) UpdateTenantControlPlaneStatus(ctx context.C
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *KubernetesIngressResource) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubernetesIngressResource) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &networkingv1.Ingress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: tenantControlPlane.GetName(),
|
||||
|
||||
@@ -25,17 +25,17 @@ type KubernetesServiceResource struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (r *KubernetesServiceResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubernetesServiceResource) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Kubernetes.Service.Name != r.resource.GetName() ||
|
||||
tenantControlPlane.Status.Kubernetes.Service.Namespace != r.resource.GetNamespace() ||
|
||||
tenantControlPlane.Status.Kubernetes.Service.Port != r.resource.Spec.Ports[0].Port
|
||||
}
|
||||
|
||||
func (r *KubernetesServiceResource) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubernetesServiceResource) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *KubernetesServiceResource) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *KubernetesServiceResource) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ func (r *KubernetesServiceResource) UpdateTenantControlPlaneStatus(ctx context.C
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *KubernetesServiceResource) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubernetesServiceResource) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: tenantControlPlane.GetName(),
|
||||
|
||||
@@ -33,12 +33,11 @@ const (
|
||||
)
|
||||
|
||||
type KubernetesDeploymentResource struct {
|
||||
resource *appsv1.Deployment
|
||||
Client client.Client
|
||||
ETCDStorageType types.ETCDStorageType
|
||||
ETCDEndpoints []string
|
||||
ETCDCompactionInterval string
|
||||
Name string
|
||||
resource *appsv1.Deployment
|
||||
Client client.Client
|
||||
ETCDStorageType types.ETCDStorageType
|
||||
ETCDEndpoints []string
|
||||
Name string
|
||||
}
|
||||
|
||||
func (r *KubernetesDeploymentResource) isStatusEqual(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
|
||||
@@ -55,7 +55,7 @@ func (r *KubeadmAddonResource) SetKubeadmConfigChecksum(checksum string) {
|
||||
r.kubeadmConfigChecksum = checksum
|
||||
}
|
||||
|
||||
func (r *KubeadmAddonResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubeadmAddonResource) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return !r.isStatusEqual(tenantControlPlane)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func (r *KubeadmAddonResource) CleanUp(ctx context.Context, tenantControlPlane *
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *KubeadmAddonResource) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubeadmAddonResource) Define(context.Context, *kamajiv1alpha1.TenantControlPlane) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ func (r *KubeadmAddonResource) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func (r *KubeadmAddonResource) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubeadmAddonResource) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
status, err := r.GetStatus(tenantControlPlane)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -19,27 +19,25 @@ import (
|
||||
)
|
||||
|
||||
type KubeadmConfigResource struct {
|
||||
resource *corev1.ConfigMap
|
||||
Client client.Client
|
||||
Name string
|
||||
ETCDs []string
|
||||
ETCDCompactionInterval string
|
||||
TmpDirectory string
|
||||
resource *corev1.ConfigMap
|
||||
Client client.Client
|
||||
ETCDs []string
|
||||
TmpDirectory string
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubeadmConfigResource) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.KubeadmConfig.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubeadmConfigResource) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *KubeadmConfigResource) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubeadmConfigResource) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -51,7 +49,7 @@ func (r *KubeadmConfigResource) Define(ctx context.Context, tenantControlPlane *
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
@@ -59,7 +57,7 @@ func (r *KubeadmConfigResource) CreateOrUpdate(ctx context.Context, tenantContro
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) GetName() string {
|
||||
return r.Name
|
||||
return "kubeadmconfig"
|
||||
}
|
||||
|
||||
func (r *KubeadmConfigResource) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
@@ -98,7 +96,6 @@ func (r *KubeadmConfigResource) mutate(tenantControlPlane *kamajiv1alpha1.Tenant
|
||||
TenantControlPlaneServiceCIDR: tenantControlPlane.Spec.NetworkProfile.ServiceCIDR,
|
||||
TenantControlPlaneVersion: tenantControlPlane.Spec.Kubernetes.Version,
|
||||
ETCDs: r.ETCDs,
|
||||
ETCDCompactionInterval: r.ETCDCompactionInterval,
|
||||
CertificatesDir: r.TmpDirectory,
|
||||
}
|
||||
|
||||
|
||||
@@ -55,19 +55,19 @@ func (r *KubeadmPhase) SetKubeadmConfigChecksum(checksum string) {
|
||||
r.checksum = checksum
|
||||
}
|
||||
|
||||
func (r *KubeadmPhase) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubeadmPhase) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return !r.isStatusEqual(tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *KubeadmPhase) ShouldCleanup(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *KubeadmPhase) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *KubeadmPhase) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *KubeadmPhase) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *KubeadmPhase) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubeadmPhase) Define(context.Context, *kamajiv1alpha1.TenantControlPlane) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (r *KubeadmPhase) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func (r *KubeadmPhase) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *KubeadmPhase) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
status, err := r.GetStatus(tenantControlPlane)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
)
|
||||
|
||||
type KubernetesUpgrade struct {
|
||||
Name string
|
||||
Client client.Client
|
||||
upgrade upgrade.Upgrade
|
||||
|
||||
@@ -47,25 +46,25 @@ func (k *KubernetesUpgrade) CleanUp(context.Context, *kamajiv1alpha1.TenantContr
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (k *KubernetesUpgrade) CreateOrUpdate(ctx context.Context, plane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
func (k *KubernetesUpgrade) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
// A new installation, no need to upgrade
|
||||
if len(plane.Status.Kubernetes.Version.Version) == 0 {
|
||||
if len(tenantControlPlane.Status.Kubernetes.Version.Version) == 0 {
|
||||
k.inProgress = false
|
||||
|
||||
return controllerutil.OperationResultNone, nil
|
||||
}
|
||||
// No version change, no need to upgrade
|
||||
if plane.Status.Kubernetes.Version.Version == plane.Spec.Kubernetes.Version {
|
||||
if tenantControlPlane.Status.Kubernetes.Version.Version == tenantControlPlane.Spec.Kubernetes.Version {
|
||||
k.inProgress = false
|
||||
|
||||
return controllerutil.OperationResultNone, nil
|
||||
}
|
||||
// An upgrade is in progress, let it go
|
||||
if status := plane.Status.Kubernetes.Version.Status; status != nil && *status == kamajiv1alpha1.VersionUpgrading {
|
||||
if status := tenantControlPlane.Status.Kubernetes.Version.Status; status != nil && *status == kamajiv1alpha1.VersionUpgrading {
|
||||
return controllerutil.OperationResultNone, nil
|
||||
}
|
||||
// Checking if the upgrade is allowed, or not
|
||||
restClient, err := utilities.GetTenantRESTClient(ctx, k.Client, plane)
|
||||
restClient, err := utilities.GetTenantRESTClient(ctx, k.Client, tenantControlPlane)
|
||||
if err != nil {
|
||||
return controllerutil.OperationResultNone, errors.Wrap(err, "cannot create REST client required for Kubernetes upgrade plan")
|
||||
}
|
||||
@@ -86,14 +85,14 @@ func (k *KubernetesUpgrade) CreateOrUpdate(ctx context.Context, plane *kamajiv1a
|
||||
}
|
||||
|
||||
func (k *KubernetesUpgrade) GetName() string {
|
||||
return k.Name
|
||||
return "upgrade"
|
||||
}
|
||||
|
||||
func (k *KubernetesUpgrade) ShouldStatusBeUpdated(context.Context, *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return k.inProgress
|
||||
}
|
||||
|
||||
func (k *KubernetesUpgrade) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (k *KubernetesUpgrade) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if k.inProgress {
|
||||
tenantControlPlane.Status.Kubernetes.Version.Status = &kamajiv1alpha1.VersionUpgrading
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func (r *KubeconfigResource) Define(ctx context.Context, tenantControlPlane *kam
|
||||
}
|
||||
|
||||
func (r *KubeconfigResource) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *KubeconfigResource) GetClient() client.Client {
|
||||
|
||||
@@ -28,19 +28,19 @@ type SACertificate struct {
|
||||
TmpDirectory string
|
||||
}
|
||||
|
||||
func (r *SACertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SACertificate) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Certificates.SA.SecretName != r.resource.GetName()
|
||||
}
|
||||
|
||||
func (r *SACertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SACertificate) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *SACertificate) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *SACertificate) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *SACertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SACertificate) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -52,7 +52,7 @@ func (r *SACertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1
|
||||
}
|
||||
|
||||
func (r *SACertificate) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *SACertificate) GetClient() client.Client {
|
||||
@@ -68,7 +68,7 @@ func (r *SACertificate) CreateOrUpdate(ctx context.Context, tenantControlPlane *
|
||||
}
|
||||
|
||||
func (r *SACertificate) GetName() string {
|
||||
return r.Name
|
||||
return "sa-certificate"
|
||||
}
|
||||
|
||||
func (r *SACertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
|
||||
@@ -5,11 +5,9 @@ package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
k8stypes "k8s.io/apimachinery/pkg/types"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
@@ -20,27 +18,26 @@ import (
|
||||
)
|
||||
|
||||
type SQLCertificate struct {
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Name string
|
||||
StorageType types.ETCDStorageType
|
||||
SQLConfigSecretName string
|
||||
SQLConfigSecretNamespace string
|
||||
resource *corev1.Secret
|
||||
Client client.Client
|
||||
Name string
|
||||
StorageType types.ETCDStorageType
|
||||
DataStore kamajiv1alpha1.DataStore
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SQLCertificate) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return tenantControlPlane.Status.Storage.Kine.Certificate.Checksum != r.resource.GetAnnotations()["checksum"]
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SQLCertificate) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *SQLCertificate) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLCertificate) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -53,7 +50,7 @@ func (r *SQLCertificate) Define(ctx context.Context, tenantControlPlane *kamajiv
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) getPrefixedName(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) string {
|
||||
return utilities.AddTenantPrefix(r.Name, tenantControlPlane)
|
||||
return utilities.AddTenantPrefix(r.GetName(), tenantControlPlane)
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) GetClient() client.Client {
|
||||
@@ -68,7 +65,7 @@ func (r *SQLCertificate) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLCertificate) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
tenantControlPlane.Status.Storage.Kine = &kamajiv1alpha1.KineStatus{}
|
||||
}
|
||||
@@ -82,15 +79,25 @@ func (r *SQLCertificate) UpdateTenantControlPlaneStatus(ctx context.Context, ten
|
||||
|
||||
func (r *SQLCertificate) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn {
|
||||
return func() error {
|
||||
sqlConfig := &corev1.Secret{}
|
||||
namespacedName := k8stypes.NamespacedName{Namespace: r.SQLConfigSecretNamespace, Name: r.SQLConfigSecretName}
|
||||
if err := r.Client.Get(ctx, namespacedName, sqlConfig); err != nil {
|
||||
return err
|
||||
ca, err := r.DataStore.Spec.TLSConfig.CertificateAuthority.Certificate.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
checksum, err := r.buildSecret(ctx, *sqlConfig)
|
||||
crt, err := r.DataStore.Spec.TLSConfig.ClientCertificate.Certificate.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
|
||||
key, err := r.DataStore.Spec.TLSConfig.ClientCertificate.PrivateKey.GetContent(ctx, r.Client)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
r.resource.Data = map[string][]byte{
|
||||
"ca.crt": ca,
|
||||
"server.crt": crt,
|
||||
"server.key": key,
|
||||
}
|
||||
|
||||
annotations := r.resource.GetAnnotations()
|
||||
@@ -98,7 +105,7 @@ func (r *SQLCertificate) mutate(ctx context.Context, tenantControlPlane *kamajiv
|
||||
annotations = map[string]string{}
|
||||
}
|
||||
|
||||
annotations["checksum"] = checksum
|
||||
annotations["checksum"] = utilities.CalculateConfigMapChecksum(r.resource.StringData)
|
||||
|
||||
r.resource.SetAnnotations(annotations)
|
||||
|
||||
@@ -114,30 +121,3 @@ func (r *SQLCertificate) mutate(ctx context.Context, tenantControlPlane *kamajiv
|
||||
return ctrl.SetControllerReference(tenantControlPlane, r.resource, r.Client.Scheme())
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) buildSecret(ctx context.Context, sqlConfig corev1.Secret) (checksum string, err error) {
|
||||
switch r.StorageType {
|
||||
case types.KineMySQL, types.KinePostgreSQL:
|
||||
keys := []string{"ca.crt", "server.crt", "server.key"}
|
||||
|
||||
return r.buildKineSecret(ctx, keys, sqlConfig)
|
||||
default:
|
||||
return "", fmt.Errorf("storage type %s is not implemented", r.StorageType)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SQLCertificate) buildKineSecret(ctx context.Context, keys []string, sqlConfig corev1.Secret) (string, error) {
|
||||
checksumMap := map[string]string{}
|
||||
|
||||
for _, key := range keys {
|
||||
value, ok := sqlConfig.Data[key]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("%s is not in sql config secret", key)
|
||||
}
|
||||
|
||||
r.resource.Data[key] = value
|
||||
checksumMap[key] = string(value)
|
||||
}
|
||||
|
||||
return utilities.CalculateConfigMapChecksum(checksumMap), nil
|
||||
}
|
||||
|
||||
@@ -27,11 +27,10 @@ type SQLSetup struct {
|
||||
resource sqlSetupResource
|
||||
Client client.Client
|
||||
DBConnection sql.DBConnection
|
||||
Name string
|
||||
Driver string
|
||||
}
|
||||
|
||||
func (r *SQLSetup) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SQLSetup) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
return true
|
||||
}
|
||||
@@ -40,11 +39,11 @@ func (r *SQLSetup) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane
|
||||
tenantControlPlane.Status.Storage.Kine.Setup.Checksum != tenantControlPlane.Status.Storage.Kine.Config.Checksum
|
||||
}
|
||||
|
||||
func (r *SQLSetup) ShouldCleanup(tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SQLSetup) ShouldCleanup(_ *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *SQLSetup) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *SQLSetup) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@@ -107,7 +106,7 @@ func (r *SQLSetup) CreateOrUpdate(ctx context.Context, tenantControlPlane *kamaj
|
||||
}
|
||||
|
||||
func (r *SQLSetup) GetName() string {
|
||||
return r.Name
|
||||
return "sql-setup"
|
||||
}
|
||||
|
||||
func (r *SQLSetup) Delete(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
@@ -130,7 +129,7 @@ func (r *SQLSetup) Delete(ctx context.Context, tenantControlPlane *kamajiv1alpha
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SQLSetup) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLSetup) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
return fmt.Errorf("sql configuration is not ready")
|
||||
}
|
||||
@@ -143,7 +142,7 @@ func (r *SQLSetup) UpdateTenantControlPlaneStatus(ctx context.Context, tenantCon
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SQLSetup) createDB(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
func (r *SQLSetup) createDB(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
exists, err := r.DBConnection.DBExists(ctx, r.resource.schema)
|
||||
if err != nil {
|
||||
return controllerutil.OperationResultNone, err
|
||||
@@ -160,7 +159,7 @@ func (r *SQLSetup) createDB(ctx context.Context, tenantControlPlane *kamajiv1alp
|
||||
return controllerutil.OperationResultCreated, nil
|
||||
}
|
||||
|
||||
func (r *SQLSetup) deleteDB(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLSetup) deleteDB(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) error {
|
||||
exists, err := r.DBConnection.DBExists(ctx, r.resource.schema)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -177,7 +176,7 @@ func (r *SQLSetup) deleteDB(ctx context.Context, tenantControlPlane *kamajiv1alp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SQLSetup) createUser(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
func (r *SQLSetup) createUser(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
exists, err := r.DBConnection.UserExists(ctx, r.resource.user)
|
||||
if err != nil {
|
||||
return controllerutil.OperationResultNone, err
|
||||
@@ -194,7 +193,7 @@ func (r *SQLSetup) createUser(ctx context.Context, tenantControlPlane *kamajiv1a
|
||||
return controllerutil.OperationResultCreated, nil
|
||||
}
|
||||
|
||||
func (r *SQLSetup) deleteUser(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLSetup) deleteUser(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) error {
|
||||
exists, err := r.DBConnection.UserExists(ctx, r.resource.user)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -211,7 +210,7 @@ func (r *SQLSetup) deleteUser(ctx context.Context, tenantControlPlane *kamajiv1a
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SQLSetup) createGrantPrivileges(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
func (r *SQLSetup) createGrantPrivileges(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) (controllerutil.OperationResult, error) {
|
||||
exists, err := r.DBConnection.GrantPrivilegesExists(ctx, r.resource.user, r.resource.schema)
|
||||
if err != nil {
|
||||
return controllerutil.OperationResultNone, err
|
||||
@@ -228,7 +227,7 @@ func (r *SQLSetup) createGrantPrivileges(ctx context.Context, tenantControlPlane
|
||||
return controllerutil.OperationResultCreated, nil
|
||||
}
|
||||
|
||||
func (r *SQLSetup) revokeGrantPrivileges(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLSetup) revokeGrantPrivileges(ctx context.Context, _ *kamajiv1alpha1.TenantControlPlane) error {
|
||||
exists, err := r.DBConnection.GrantPrivilegesExists(ctx, r.resource.user, r.resource.schema)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -26,7 +26,7 @@ type SQLStorageConfig struct {
|
||||
Driver string
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) ShouldStatusBeUpdated(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SQLStorageConfig) ShouldStatusBeUpdated(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
return true
|
||||
}
|
||||
@@ -35,15 +35,15 @@ func (r *SQLStorageConfig) ShouldStatusBeUpdated(ctx context.Context, tenantCont
|
||||
tenantControlPlane.Status.Storage.Kine.Driver != r.Driver
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) ShouldCleanup(plane *kamajiv1alpha1.TenantControlPlane) bool {
|
||||
func (r *SQLStorageConfig) ShouldCleanup(*kamajiv1alpha1.TenantControlPlane) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) CleanUp(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
func (r *SQLStorageConfig) CleanUp(context.Context, *kamajiv1alpha1.TenantControlPlane) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) Define(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLStorageConfig) Define(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
r.resource = &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: r.getPrefixedName(tenantControlPlane),
|
||||
@@ -70,7 +70,7 @@ func (r *SQLStorageConfig) GetName() string {
|
||||
return r.Name
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) UpdateTenantControlPlaneStatus(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
func (r *SQLStorageConfig) UpdateTenantControlPlaneStatus(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) error {
|
||||
if tenantControlPlane.Status.Storage.Kine == nil {
|
||||
tenantControlPlane.Status.Storage.Kine = &kamajiv1alpha1.KineStatus{}
|
||||
}
|
||||
@@ -82,7 +82,7 @@ func (r *SQLStorageConfig) UpdateTenantControlPlaneStatus(ctx context.Context, t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *SQLStorageConfig) mutate(ctx context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn {
|
||||
func (r *SQLStorageConfig) mutate(_ context.Context, tenantControlPlane *kamajiv1alpha1.TenantControlPlane) controllerutil.MutateFn {
|
||||
return func() error {
|
||||
var password []byte
|
||||
|
||||
|
||||
14
kamaji.yaml
14
kamaji.yaml
@@ -1,16 +1,6 @@
|
||||
etcd-storage-type: etcd
|
||||
etcd-ca-secret-name: "etcd-certs"
|
||||
etcd-ca-secret-namespace: kamaji-system
|
||||
etcd-endpoints: https://etcd-0.etcd.kamaji-system.svc.cluster.local:2379,https://etcd-1.etcd.kamaji-system.svc.cluster.local:2379,https://etcd-2.etcd.kamaji-system.svc.cluster.local:2379
|
||||
etcd-client-secret-name: root-client-certs
|
||||
etcd-client-secret-namespaced: kamaji-system
|
||||
etcd-compaction: "0"
|
||||
metrics-bind-address: :8080
|
||||
health-probe-bind-address: :8081
|
||||
leader-elect: false
|
||||
tmp-directory: /tmp/kamaji
|
||||
kine-mysql-secret-name: "mysql-config"
|
||||
kine-mysql-secret-namespace: kamaji-system
|
||||
kine-mysql-host: localhost
|
||||
kine-mysql-port: 3306
|
||||
kine-image: rancher/kine:v0.9.2-amd64
|
||||
datastore: etcd
|
||||
kine-image: rancher/kine:v0.9.2-amd64
|
||||
|
||||
25
main.go
25
main.go
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/clastix/kamaji/controllers"
|
||||
"github.com/clastix/kamaji/internal"
|
||||
"github.com/clastix/kamaji/internal/config"
|
||||
"github.com/clastix/kamaji/internal/types"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -60,24 +59,22 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
tcpChannel := make(controllers.TenantControlPlaneChannel)
|
||||
|
||||
if err = (&controllers.DataStore{TenantControlPlaneTrigger: tcpChannel, ResourceName: conf.GetString("datastore")}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "DataStore")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
reconciler := &controllers.TenantControlPlaneReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
Config: controllers.TenantControlPlaneReconcilerConfig{
|
||||
ETCDStorageType: types.ParseETCDStorageType(conf.GetString("etcd-storage-type")),
|
||||
ETCDCASecretName: conf.GetString("etcd-ca-secret-name"),
|
||||
ETCDCASecretNamespace: conf.GetString("etcd-ca-secret-namespace"),
|
||||
ETCDClientSecretName: conf.GetString("etcd-client-secret-name"),
|
||||
ETCDClientSecretNamespace: conf.GetString("etcd-client-secret-namespace"),
|
||||
ETCDEndpoints: types.ParseETCDEndpoint(conf),
|
||||
ETCDCompactionInterval: conf.GetString("etcd-compaction-interval"),
|
||||
TmpBaseDirectory: conf.GetString("tmp-directory"),
|
||||
KineSecretName: conf.GetString("kine-secret-name"),
|
||||
KineSecretNamespace: conf.GetString("kine-secret-namespace"),
|
||||
KineHost: conf.GetString("kine-host"),
|
||||
KinePort: conf.GetInt("kine-port"),
|
||||
KineContainerImage: conf.GetString("kine-image"),
|
||||
DataStoreName: conf.GetString("datastore"),
|
||||
KineContainerImage: conf.GetString("kine-image"),
|
||||
TmpBaseDirectory: conf.GetString("tmp-directory"),
|
||||
},
|
||||
TriggerChan: tcpChannel,
|
||||
}
|
||||
|
||||
if err := reconciler.SetupWithManager(mgr); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user