mirror of
https://github.com/labring/sealos.git
synced 2026-09-19 02:23:07 +08:00
feat(devbox): add startup configmap synchronization and volume manage… (#6175)
feat(devbox): add startup configmap synchronization and volume management (#6172) * feat(devbox): add support for startup config map and related volume mounts * Update controllers/devbox/internal/controller/devbox_controller.go * Update controllers/devbox/internal/controller/devbox_controller.go * Update controllers/devbox/internal/controller/helper/devbox.go * Update controllers/devbox/internal/controller/helper/devbox.go * fix(devbox): ensure both DEVBOX_STARTUP_CM_NAME and DEVBOX_STARTUP_CM_NAMESPACE are set together * fix(devbox): update startup.sh check to ensure configmap data consistency * fix(devbox): ensure devboxConfigmap data is initialized (#877) --------- Signed-off-by: Yun Pan <dinoallo@netc.it> Co-authored-by: cuisongliu <cuisongliu@qq.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
cuisongliu
Copilot
parent
fff9215f64
commit
9d48c1852f
@@ -236,6 +236,13 @@ func main() {
|
||||
podMatchers = append(podMatchers, matcher.EphemeralStorageMatcher{})
|
||||
}
|
||||
|
||||
startupCMName := os.Getenv("DEVBOX_STARTUP_CM_NAME")
|
||||
startupCMNamespace := os.Getenv("DEVBOX_STARTUP_CM_NAMESPACE")
|
||||
if (startupCMName != "" && startupCMNamespace == "") || (startupCMName == "" && startupCMNamespace != "") {
|
||||
setupLog.Error(nil, "both DEVBOX_STARTUP_CM_NAME and DEVBOX_STARTUP_CM_NAMESPACE must be set together")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.DevboxReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
@@ -250,9 +257,11 @@ func main() {
|
||||
DefaultLimit: resource.MustParse(limitEphemeralStorage),
|
||||
MaximumLimit: resource.MustParse(maximumLimitEphemeralStorage),
|
||||
},
|
||||
PodMatchers: podMatchers,
|
||||
DebugMode: debugMode,
|
||||
RestartPredicateDuration: restartPredicateDuration,
|
||||
PodMatchers: podMatchers,
|
||||
DebugMode: debugMode,
|
||||
StartupConfigMapName: startupCMName,
|
||||
StartupConfigMapNamespace: startupCMNamespace,
|
||||
RestartPredicateDuration: restartPredicateDuration,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Devbox")
|
||||
os.Exit(1)
|
||||
|
||||
@@ -18,6 +18,12 @@ kind: ClusterRole
|
||||
metadata:
|
||||
name: manager-role
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
||||
@@ -3379,6 +3379,12 @@ rules:
|
||||
- secrets
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
|
||||
@@ -56,7 +56,9 @@ type DevboxReconciler struct {
|
||||
|
||||
PodMatchers []matcher.PodMatcher
|
||||
|
||||
DebugMode bool
|
||||
DebugMode bool
|
||||
StartupConfigMapName string
|
||||
StartupConfigMapNamespace string
|
||||
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
@@ -73,6 +75,7 @@ type DevboxReconciler struct {
|
||||
// +kubebuilder:rbac:groups="",resources=pods/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups="",resources=services,verbs=*
|
||||
// +kubebuilder:rbac:groups="",resources=secrets,verbs=*
|
||||
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=*
|
||||
// +kubebuilder:rbac:groups="",resources=events,verbs=*
|
||||
|
||||
func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
@@ -89,6 +92,10 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
|
||||
PartOf: devboxv1alpha1.DevBoxPartOf,
|
||||
})
|
||||
|
||||
logger.Info("start reconciling devbox", "devbox", devbox.Name)
|
||||
if r.StartupConfigMapName != "" {
|
||||
logger.Info("startup config map set", "startupConfigMapName", r.StartupConfigMapName, "startupConfigMapNamespace", r.StartupConfigMapNamespace)
|
||||
}
|
||||
if devbox.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
// retry add finalizer
|
||||
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
@@ -132,6 +139,18 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
|
||||
logger.Info("sync secret success")
|
||||
r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync secret success", "Sync secret success")
|
||||
|
||||
if r.StartupConfigMapName != "" {
|
||||
// create or update startup configmap
|
||||
logger.Info("syncing startup configmap")
|
||||
if err := r.syncStartupConfigMap(ctx, devbox, recLabels); err != nil {
|
||||
logger.Error(err, "sync startup configmap failed")
|
||||
r.Recorder.Eventf(devbox, corev1.EventTypeWarning, "Sync startup configmap failed", "%v", err)
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
logger.Info("sync startup configmap success")
|
||||
r.Recorder.Eventf(devbox, corev1.EventTypeNormal, "Sync startup configmap success", "Sync startup configmap success")
|
||||
}
|
||||
|
||||
// create service if network type is NodePort
|
||||
if devbox.Spec.NetworkSpec.Type == devboxv1alpha1.NetworkTypeNodePort {
|
||||
logger.Info("syncing service")
|
||||
@@ -161,6 +180,60 @@ func (r *DevboxReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctr
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
func (r *DevboxReconciler) syncStartupConfigMap(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error {
|
||||
objectMeta := metav1.ObjectMeta{
|
||||
Name: devbox.Name,
|
||||
Namespace: devbox.Namespace,
|
||||
Labels: recLabels,
|
||||
}
|
||||
devboxConfigmap := &corev1.ConfigMap{
|
||||
ObjectMeta: objectMeta,
|
||||
}
|
||||
|
||||
startupConfigMap := &corev1.ConfigMap{}
|
||||
err := r.Get(ctx, client.ObjectKey{Namespace: r.StartupConfigMapNamespace, Name: r.StartupConfigMapName}, startupConfigMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get startup configmap: %w", err)
|
||||
}
|
||||
if startupConfigMap.Data == nil || startupConfigMap.Data["startup.sh"] == "" {
|
||||
return fmt.Errorf("startup configmap %s/%s is missing the 'startup.sh' key or it is empty", r.StartupConfigMapNamespace, r.StartupConfigMapName)
|
||||
}
|
||||
err = r.Get(ctx, client.ObjectKey{Namespace: devbox.Namespace, Name: devbox.Name}, devboxConfigmap)
|
||||
if err == nil {
|
||||
// configmap already exists, no need to create
|
||||
if devboxConfigmap.Data == nil {
|
||||
devboxConfigmap.Data = make(map[string]string)
|
||||
}
|
||||
if _, ok := devboxConfigmap.Data["startup.sh"]; !ok || devboxConfigmap.Data["startup.sh"] != startupConfigMap.Data["startup.sh"] {
|
||||
devboxConfigmap.Data["startup.sh"] = startupConfigMap.Data["startup.sh"]
|
||||
if err := r.Update(ctx, devboxConfigmap); err != nil {
|
||||
return fmt.Errorf("failed to update configmap: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
if client.IgnoreNotFound(err) != nil {
|
||||
return fmt.Errorf("failed to get configmap: %w", err)
|
||||
}
|
||||
|
||||
configmap := &corev1.ConfigMap{
|
||||
ObjectMeta: objectMeta,
|
||||
Data: map[string]string{
|
||||
"startup.sh": startupConfigMap.Data["startup.sh"],
|
||||
},
|
||||
}
|
||||
|
||||
if err := controllerutil.SetControllerReference(devbox, configmap, r.Scheme); err != nil {
|
||||
return fmt.Errorf("failed to set controller reference: %w", err)
|
||||
}
|
||||
|
||||
if err := r.Create(ctx, configmap); err != nil {
|
||||
return fmt.Errorf("failed to create configmap: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *DevboxReconciler) syncSecret(ctx context.Context, devbox *devboxv1alpha1.Devbox, recLabels map[string]string) error {
|
||||
objectMeta := metav1.ObjectMeta{
|
||||
Name: devbox.Name,
|
||||
@@ -522,6 +595,10 @@ func (r *DevboxReconciler) removeAll(ctx context.Context, devbox *devboxv1alpha1
|
||||
if err := r.deleteResourcesByLabels(ctx, &corev1.Service{}, devbox.Namespace, recLabels); err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete Configmap
|
||||
if err := r.deleteResourcesByLabels(ctx, &corev1.ConfigMap{}, devbox.Namespace, recLabels); err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete Secret
|
||||
return r.deleteResourcesByLabels(ctx, &corev1.Secret{}, devbox.Namespace, recLabels)
|
||||
}
|
||||
@@ -559,9 +636,15 @@ func (r *DevboxReconciler) generateDevboxPod(devbox *devboxv1alpha1.Devbox, next
|
||||
|
||||
volumes := devbox.Spec.Config.Volumes
|
||||
volumes = append(volumes, helper.GenerateSSHVolume(devbox))
|
||||
if r.StartupConfigMapName != "" {
|
||||
volumes = append(volumes, helper.GenerateStartupVolume(devbox))
|
||||
}
|
||||
|
||||
volumeMounts := devbox.Spec.Config.VolumeMounts
|
||||
volumeMounts = append(volumeMounts, helper.GenerateSSHVolumeMounts()...)
|
||||
if r.StartupConfigMapName != "" {
|
||||
volumeMounts = append(volumeMounts, helper.GenerateStartupVolumeMounts()...)
|
||||
}
|
||||
|
||||
containers := []corev1.Container{
|
||||
{
|
||||
|
||||
@@ -326,6 +326,32 @@ func GenerateSSHVolume(devbox *devboxv1alpha1.Devbox) corev1.Volume {
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateStartupVolume generates a volume for the startup script configmap
|
||||
func GenerateStartupVolume(devbox *devboxv1alpha1.Devbox) corev1.Volume {
|
||||
return corev1.Volume{
|
||||
Name: "devbox-startup",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
ConfigMap: &corev1.ConfigMapVolumeSource{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: devbox.Name,
|
||||
},
|
||||
DefaultMode: ptr.To(int32(0755)),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
// GenerateStartupVolumeMounts generates volume mounts for the startup script
|
||||
func GenerateStartupVolumeMounts() []corev1.VolumeMount {
|
||||
return []corev1.VolumeMount{
|
||||
{
|
||||
Name: "devbox-startup",
|
||||
MountPath: "/usr/start/startup.sh",
|
||||
SubPath: "startup.sh",
|
||||
ReadOnly: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateResourceRequirements generates the resource requirements for the Devbox pod
|
||||
func GenerateResourceRequirements(devbox *devboxv1alpha1.Devbox, requestRate utilsresource.RequestRate, ephemeralStorage utilsresource.EphemeralStorage) corev1.ResourceRequirements {
|
||||
return corev1.ResourceRequirements{
|
||||
|
||||
Reference in New Issue
Block a user