Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: support inject envFrom by secret #8115

Merged
merged 5 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apis/apps/v1alpha1/config_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ func (configSpec *ComponentConfigSpec) InjectEnvEnabled() bool {
return len(configSpec.AsEnvFrom) > 0 || len(configSpec.InjectEnvTo) > 0
}

func (configSpec *ComponentConfigSpec) ToSecret() bool {
return configSpec.AsSecret != nil && *configSpec.AsSecret
}

func (configSpec *ComponentConfigSpec) ContainersInjectedTo() []string {
if len(configSpec.InjectEnvTo) != 0 {
return configSpec.InjectEnvTo
Expand Down
7 changes: 6 additions & 1 deletion apis/apps/v1alpha1/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ type ComponentTemplateSpec struct {
// template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
// The volume name must be defined in podSpec.containers[*].volumeMounts.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MaxLength=63
// +kubebuilder:validation:Pattern:=`^[a-z]([a-z0-9\-]*[a-z0-9])?$`
// +optional
VolumeName string `json:"volumeName"`

// The operator attempts to set default file permissions for scripts (0555) and configurations (0444).
Expand Down Expand Up @@ -198,6 +198,11 @@ type ComponentConfigSpec struct {
// +listType=set
// +optional
ReRenderResourceTypes []RerenderResourceType `json:"reRenderResourceTypes,omitempty"`

// Whether to store the final rendered parameters as a secret.
//
// +optional
AsSecret *bool `json:"asSecret,omitempty"`
}

// RerenderResourceType defines the resource requirements for a component.
Expand Down
5 changes: 5 additions & 0 deletions apis/apps/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ spec:
type: string
type: array
x-kubernetes-list-type: set
asSecret:
description: Whether to store the final rendered parameters
as a secret.
type: boolean
constraintRef:
description: Specifies the name of the referenced configuration
constraints object.
Expand Down Expand Up @@ -289,7 +293,6 @@ spec:
type: string
required:
- name
- volumeName
type: object
type: array
x-kubernetes-list-map-keys:
Expand Down Expand Up @@ -11638,7 +11641,6 @@ spec:
type: string
required:
- name
- volumeName
type: object
type: array
x-kubernetes-list-map-keys:
Expand Down
5 changes: 4 additions & 1 deletion config/crd/bases/apps.kubeblocks.io_configurations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ spec:
type: string
type: array
x-kubernetes-list-type: set
asSecret:
description: Whether to store the final rendered parameters
as a secret.
type: boolean
constraintRef:
description: Specifies the name of the referenced configuration
constraints object.
Expand Down Expand Up @@ -286,7 +290,6 @@ spec:
type: string
required:
- name
- volumeName
type: object
importTemplateRef:
description: |-
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ spec:
type: string
type: array
x-kubernetes-list-type: set
asSecret:
description: Whether to store the final rendered parameters
as a secret.
type: boolean
constraintRef:
description: Specifies the name of the referenced configuration
constraints object.
Expand Down Expand Up @@ -289,7 +293,6 @@ spec:
type: string
required:
- name
- volumeName
type: object
type: array
x-kubernetes-list-map-keys:
Expand Down Expand Up @@ -11638,7 +11641,6 @@ spec:
type: string
required:
- name
- volumeName
type: object
type: array
x-kubernetes-list-map-keys:
Expand Down
5 changes: 4 additions & 1 deletion deploy/helm/crds/apps.kubeblocks.io_configurations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ spec:
type: string
type: array
x-kubernetes-list-type: set
asSecret:
description: Whether to store the final rendered parameters
as a secret.
type: boolean
constraintRef:
description: Specifies the name of the referenced configuration
constraints object.
Expand Down Expand Up @@ -286,7 +290,6 @@ spec:
type: string
required:
- name
- volumeName
type: object
importTemplateRef:
description: |-
Expand Down
13 changes: 13 additions & 0 deletions docs/developer_docs/api-reference/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -6120,6 +6120,18 @@ or cluster topology. Examples:</p>
</ul>
</td>
</tr>
<tr>
<td>
<code>asSecret</code><br/>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>Whether to store the final rendered parameters as a secret.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="apps.kubeblocks.io/v1alpha1.ComponentDefinitionSpec">ComponentDefinitionSpec
Expand Down Expand Up @@ -7944,6 +7956,7 @@ string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Refers to the volume name of PodTemplate. The configuration file produced through the configuration
template will be mounted to the corresponding volume. Must be a DNS_LABEL name.
The volume name must be defined in podSpec.containers[*].volumeMounts.</p>
Expand Down
4 changes: 2 additions & 2 deletions pkg/constant/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ const (
)

// GetKBConfigMapWellKnownLabels returns the well-known labels for KB ConfigMap
func GetKBConfigMapWellKnownLabels(cmTplName, clusterDefName, clusterName, componentName string) map[string]string {
func GetKBConfigMapWellKnownLabels(cmTplName, componentDefName, clusterName, componentName string) map[string]string {
return map[string]string{
CMTemplateNameLabelKey: cmTplName,
AppNameLabelKey: clusterDefName,
AppNameLabelKey: componentDefName,
AppInstanceLabelKey: clusterName,
KBAppComponentLabelKey: componentName,
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/controller/configuration/config_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,11 @@ import (
viper "github.com/apecloud/kubeblocks/pkg/viperx"
)

func createConfigObjects(cli client.Client, ctx context.Context, objs []client.Object) error {
func createConfigObjects(cli client.Client, ctx context.Context, objs []client.Object, secretObjs []client.Object) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secretObjs => excludeObjs

for _, obj := range objs {
if slices.Contains(secretObjs, obj) {
continue
}
if err := cli.Create(ctx, obj, inDataContext()); err != nil {
if !apierrors.IsAlreadyExists(err) {
return err
Expand Down
131 changes: 83 additions & 48 deletions pkg/controller/configuration/envfrom_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,34 @@ func injectTemplateEnvFrom(cluster *appsv1alpha1.Cluster, component *component.S
var err error
var cm *corev1.ConfigMap

withEnvSource := func(asSecret bool) func(name string) corev1.EnvFromSource {
return func(name string) corev1.EnvFromSource {
if asSecret {
return corev1.EnvFromSource{
SecretRef: &corev1.SecretEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: name,
}}}
}
return corev1.EnvFromSource{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: name,
}}}
}
}

injectConfigmap := func(envMap map[string]string, configSpec appsv1alpha1.ComponentConfigSpec, cmName string) error {
envConfigMap, err := createEnvFromConfigmap(cluster, component.Name, configSpec, client.ObjectKeyFromObject(cm), envMap, ctx, cli)
envSourceObject, err := createOrUpdateResourceFromConfigTemplate(cluster, component, configSpec, client.ObjectKeyFromObject(cm), envMap, ctx, cli, true)
if err != nil {
return core.WrapError(err, "failed to generate env configmap[%s]", cmName)
}
injectEnvFrom(podSpec.Containers, configSpec.ContainersInjectedTo(), envConfigMap.Name)
injectEnvFrom(podSpec.InitContainers, configSpec.ContainersInjectedTo(), envConfigMap.Name)
if configSpec.ToSecret() && configSpec.VolumeName != "" {
podSpec.Volumes = updateSecretVolumes(podSpec.Volumes, configSpec, envSourceObject, component)
} else {
injectEnvFrom(podSpec.Containers, configSpec.ContainersInjectedTo(), envSourceObject.GetName(), withEnvSource(configSpec.ToSecret()))
injectEnvFrom(podSpec.InitContainers, configSpec.ContainersInjectedTo(), envSourceObject.GetName(), withEnvSource(configSpec.ToSecret()))
}
return nil
}

Expand Down Expand Up @@ -78,6 +99,23 @@ func injectTemplateEnvFrom(cluster *appsv1alpha1.Cluster, component *component.S
return nil
}

func updateSecretVolumes(volumes []corev1.Volume, configSpec appsv1alpha1.ComponentConfigSpec, secret client.Object, component *component.SynthesizedComponent) []corev1.Volume {
sets := configSetFromComponent(component.ConfigTemplates)
createFn := func(_ string) corev1.Volume {
return corev1.Volume{
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: secret.GetName(),
DefaultMode: intctrlutil.BuildVolumeMode(sets, configSpec.ComponentTemplateSpec),
},
},
Name: configSpec.VolumeName,
}
}
volumes, _ = intctrlutil.CreateOrUpdateVolume(volumes, configSpec.VolumeName, createFn, nil)
return volumes
}

func getConfigConstraint(template appsv1alpha1.ComponentConfigSpec, cli client.Client, ctx context.Context) (*appsv1beta1.ConfigConstraintSpec, error) {
ccKey := client.ObjectKey{
Namespace: "",
Expand Down Expand Up @@ -127,27 +165,46 @@ func fetchConfigmap(localObjs []client.Object, cmName, namespace string, cli cli
return cmObj, nil
}

func createEnvFromConfigmap(cluster *appsv1alpha1.Cluster, componentName string, template appsv1alpha1.ComponentConfigSpec, originKey client.ObjectKey, envMap map[string]string, ctx context.Context, cli client.Client) (*corev1.ConfigMap, error) {
func createOrUpdateResourceFromConfigTemplate(cluster *appsv1alpha1.Cluster, component *component.SynthesizedComponent, template appsv1alpha1.ComponentConfigSpec, originKey client.ObjectKey, envMap map[string]string, ctx context.Context, cli client.Client, createOnly bool) (client.Object, error) {
cmKey := client.ObjectKey{
Name: core.GenerateEnvFromName(originKey.Name),
Namespace: originKey.Namespace,
}
cm := &corev1.ConfigMap{}
err := cli.Get(ctx, cmKey, cm, inDataContext())
if err == nil {
return cm, nil

updateObjectMeta := func(obj client.Object) {
obj.SetLabels(constant.GetKBConfigMapWellKnownLabels(template.Name, component.CompDefName, component.ClusterName, component.Name))
_ = intctrlutil.SetOwnerReference(cluster, obj)
}
if !apierrors.IsNotFound(err) {
return nil, err

if template.ToSecret() {
return updateOrCreateEnvObject(ctx, cli, &corev1.Secret{}, cmKey, func(c *corev1.Secret) {
c.StringData = envMap
updateObjectMeta(c)
}, createOnly)
}
cm.Name = cmKey.Name
cm.Namespace = cmKey.Namespace
cm.Data = envMap
cm.Labels = constant.GetKBConfigMapWellKnownLabels(template.Name, cluster.Spec.ClusterDefRef, cluster.Name, componentName)
if err := intctrlutil.SetOwnerReference(cluster, cm); err != nil {
return nil, err
return updateOrCreateEnvObject(ctx, cli, &corev1.ConfigMap{}, cmKey, func(c *corev1.ConfigMap) {
c.Data = envMap
updateObjectMeta(c)
}, createOnly)
}

func updateOrCreateEnvObject[T generics.Object, PT generics.PObject[T]](ctx context.Context, cli client.Client, obj PT, objKey client.ObjectKey, updater func(PT), createOnly bool) (client.Object, error) {
err := cli.Get(ctx, objKey, obj, inDataContext())
switch {
case err != nil:
if !apierrors.IsNotFound(err) {
return nil, err
}
obj.SetName(objKey.Name)
obj.SetNamespace(objKey.Namespace)
updater(obj)
return obj, cli.Create(ctx, obj, inDataContext())
case err == nil && createOnly:
return obj, nil
default:
updater(obj)
return obj, cli.Update(ctx, obj, inDataContext())
}
return cm, cli.Create(ctx, cm, inDataContext())
}

func CheckEnvFrom(container *corev1.Container, cmName string) bool {
Expand All @@ -156,22 +213,19 @@ func CheckEnvFrom(container *corev1.Container, cmName string) bool {
if source.ConfigMapRef != nil && source.ConfigMapRef.Name == cmName {
return true
}
if source.SecretRef != nil && source.SecretRef.Name == cmName {
return true
}
}
return false
}

func injectEnvFrom(containers []corev1.Container, injectEnvTo []string, cmName string) {
func injectEnvFrom(containers []corev1.Container, injectEnvTo []string, cmName string, fn func(string) corev1.EnvFromSource) {
sets := cfgutil.NewSet(injectEnvTo...)
for i := range containers {
container := &containers[i]
if sets.InArray(container.Name) && !CheckEnvFrom(container, cmName) {
container.EnvFrom = append(container.EnvFrom,
corev1.EnvFromSource{
ConfigMapRef: &corev1.ConfigMapEnvSource{
LocalObjectReference: corev1.LocalObjectReference{
Name: cmName,
}},
})
container.EnvFrom = append(container.EnvFrom, fn(cmName))
}
}
}
Expand All @@ -196,35 +250,16 @@ func fromConfigSpec(configSpec appsv1alpha1.ComponentConfigSpec, cm *corev1.Conf
return keys
}

func SyncEnvConfigmap(configSpec appsv1alpha1.ComponentConfigSpec, cmObj *corev1.ConfigMap, cc *appsv1beta1.ConfigConstraintSpec, cli client.Client, ctx context.Context) error {
func SyncEnvConfigmap(configSpec appsv1alpha1.ComponentConfigSpec, cmObj *corev1.ConfigMap, cc *appsv1beta1.ConfigConstraintSpec, cli client.Client, ctx context.Context, cluster *appsv1alpha1.Cluster, component *component.SynthesizedComponent) error {
if !configSpec.InjectEnvEnabled() || cc == nil || cc.FileFormatConfig == nil {
return nil
}
envMap, err := fromConfigmapFiles(fromConfigSpec(configSpec, cmObj), cmObj, cc.FileFormatConfig)
if err != nil {
return err
}
if len(envMap) == 0 {
return nil
if len(envMap) != 0 {
_, err = createOrUpdateResourceFromConfigTemplate(cluster, component, configSpec, client.ObjectKeyFromObject(cmObj), envMap, ctx, cli, false)
}

return updateEnvFromConfigmap(client.ObjectKeyFromObject(cmObj), envMap, cli, ctx)
}

// TODO(leon)
func updateEnvFromConfigmap(origObj client.ObjectKey, envMap map[string]string, cli client.Client, ctx context.Context) error {
cmKey := client.ObjectKey{
Name: core.GenerateEnvFromName(origObj.Name),
Namespace: origObj.Namespace,
}
cm := &corev1.ConfigMap{}
if err := cli.Get(ctx, cmKey, cm); err != nil {
return err
}
patch := client.MergeFrom(cm.DeepCopy())
cm.Data = envMap
if err := cli.Patch(ctx, cm, patch); err != nil {
return err
}
return nil
return err
}
Loading
Loading