feat: kubeconfig generator (#933)

* feat(api): kubeconfig generator

Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>

* refactor: abstracting enqueue to channel function

Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>

* fix: avoiding multiple context registration

Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>

* feat: kubeconfig generator

Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>

* docs: kubeconfig generator

Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>

* feat(helm): deployment for kubeconfig generator

Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>

---------

Signed-off-by: Dario Tranchitella <dario@tranchitella.eu>
This commit is contained in:
Dario Tranchitella
2025-09-22 15:32:50 +02:00
committed by GitHub
parent 4bace03fc3
commit cb2152d5a7
22 changed files with 2108 additions and 15 deletions

View File

@@ -0,0 +1,91 @@
// Copyright 2022 Clastix Labs
// SPDX-License-Identifier: Apache-2.0
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var (
ManagedByLabel = "kamaji.clastix.io/managed-by"
ManagedForLabel = "kamaji.clastix.io/managed-for"
)
//+kubebuilder:object:root=true
//+kubebuilder:subresource:status
//+kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Age"
//+kubebuilder:metadata:annotations={"cert-manager.io/inject-ca-from=kamaji-system/kamaji-serving-cert"}
//+kubebuilder:resource:scope=Cluster,shortName=kc,categories=kamaji
// KubeconfigGenerator is the Schema for the kubeconfiggenerators API.
type KubeconfigGenerator struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec KubeconfigGeneratorSpec `json:"spec,omitempty"`
Status KubeconfigGeneratorStatus `json:"status,omitempty"`
}
// CompoundValue allows defining a static, or a dynamic value.
// Options are mutually exclusive, just one should be picked up.
// +kubebuilder:validation:XValidation:rule="(has(self.stringValue) || has(self.fromDefinition)) && !(has(self.stringValue) && has(self.fromDefinition))",message="Either stringValue or fromDefinition must be set, but not both."
type CompoundValue struct {
// StringValue is a static string value.
StringValue string `json:"stringValue,omitempty"`
// FromDefinition is used to generate a dynamic value,
// it uses the dot notation to access fields from the referenced TenantControlPlane object:
// e.g.: metadata.name
FromDefinition string `json:"fromDefinition,omitempty"`
}
type KubeconfigGeneratorSpec struct {
// NamespaceSelector is used to filter Namespaces from which the generator should extract TenantControlPlane objects.
NamespaceSelector metav1.LabelSelector `json:"namespaceSelector,omitempty"`
// TenantControlPlaneSelector is used to filter the TenantControlPlane objects that should be address by the generator.
TenantControlPlaneSelector metav1.LabelSelector `json:"tenantControlPlaneSelector,omitempty"`
// Groups is resolved a set of strings used to assign the x509 organisations field.
// It will be recognised by Kubernetes as user groups.
Groups []CompoundValue `json:"groups,omitempty"`
// User resolves to a string to identify the client, assigned to the x509 Common Name field.
User CompoundValue `json:"user"`
// ControlPlaneEndpointFrom is the key used to extract the Tenant Control Plane endpoint that must be used by the generator.
// The targeted Secret is the `${TCP}-admin-kubeconfig` one, default to `admin.svc`.
//+kubebuilder:default="admin.svc"
ControlPlaneEndpointFrom string `json:"controlPlaneEndpointFrom,omitempty"`
}
type KubeconfigGeneratorStatusError struct {
// Resource is the Namespaced name of the errored resource.
//+kubebuilder:validation:Required
Resource string `json:"resource"`
// Message is the error message recorded upon the last generator run.
//+kubebuilder:validation:Required
Message string `json:"message"`
}
// KubeconfigGeneratorStatus defines the observed state of KubeconfigGenerator.
type KubeconfigGeneratorStatus struct {
// Resources is the sum of targeted TenantControlPlane objects.
//+kubebuilder:default=0
Resources int `json:"resources"`
// AvailableResources is the sum of successfully generated resources.
// In case of a different value compared to Resources, check the field errors.
//+kubebuilder:default=0
AvailableResources int `json:"availableResources"`
// Errors is the list of failed kubeconfig generations.
Errors []KubeconfigGeneratorStatusError `json:"errors,omitempty"`
}
//+kubebuilder:object:root=true
// KubeconfigGeneratorList contains a list of TenantControlPlane.
type KubeconfigGeneratorList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []KubeconfigGenerator `json:"items"`
}
func init() {
SchemeBuilder.Register(&KubeconfigGenerator{}, &KubeconfigGeneratorList{})
}

View File

@@ -289,6 +289,21 @@ func (in *ClientCertificate) DeepCopy() *ClientCertificate {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CompoundValue) DeepCopyInto(out *CompoundValue) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CompoundValue.
func (in *CompoundValue) DeepCopy() *CompoundValue {
if in == nil {
return nil
}
out := new(CompoundValue)
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
@@ -951,6 +966,123 @@ func (in *KubeadmPhasesStatus) DeepCopy() *KubeadmPhasesStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeconfigGenerator) DeepCopyInto(out *KubeconfigGenerator) {
*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 KubeconfigGenerator.
func (in *KubeconfigGenerator) DeepCopy() *KubeconfigGenerator {
if in == nil {
return nil
}
out := new(KubeconfigGenerator)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubeconfigGenerator) 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 *KubeconfigGeneratorList) DeepCopyInto(out *KubeconfigGeneratorList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]KubeconfigGenerator, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigGeneratorList.
func (in *KubeconfigGeneratorList) DeepCopy() *KubeconfigGeneratorList {
if in == nil {
return nil
}
out := new(KubeconfigGeneratorList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *KubeconfigGeneratorList) 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 *KubeconfigGeneratorSpec) DeepCopyInto(out *KubeconfigGeneratorSpec) {
*out = *in
in.NamespaceSelector.DeepCopyInto(&out.NamespaceSelector)
in.TenantControlPlaneSelector.DeepCopyInto(&out.TenantControlPlaneSelector)
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]CompoundValue, len(*in))
copy(*out, *in)
}
out.User = in.User
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigGeneratorSpec.
func (in *KubeconfigGeneratorSpec) DeepCopy() *KubeconfigGeneratorSpec {
if in == nil {
return nil
}
out := new(KubeconfigGeneratorSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeconfigGeneratorStatus) DeepCopyInto(out *KubeconfigGeneratorStatus) {
*out = *in
if in.Errors != nil {
in, out := &in.Errors, &out.Errors
*out = make([]KubeconfigGeneratorStatusError, len(*in))
copy(*out, *in)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigGeneratorStatus.
func (in *KubeconfigGeneratorStatus) DeepCopy() *KubeconfigGeneratorStatus {
if in == nil {
return nil
}
out := new(KubeconfigGeneratorStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeconfigGeneratorStatusError) DeepCopyInto(out *KubeconfigGeneratorStatusError) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeconfigGeneratorStatusError.
func (in *KubeconfigGeneratorStatusError) DeepCopy() *KubeconfigGeneratorStatusError {
if in == nil {
return nil
}
out := new(KubeconfigGeneratorStatusError)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubeconfigStatus) DeepCopyInto(out *KubeconfigStatus) {
*out = *in