diff --git a/api/v1/devcontainerapp_types.go b/api/v1/devcontainerapp_types.go index 7ed34e6..0aac191 100644 --- a/api/v1/devcontainerapp_types.go +++ b/api/v1/devcontainerapp_types.go @@ -94,6 +94,10 @@ type DevcontainerAppStatus struct { // Information when was the last time the job was successfully scheduled. // +optional LastScheduleTime *metav1.Time `json:"lastScheduleTime,omitempty"` + + // NodePortAssigned 存储 DevcontainerApp CRD调度后集群分配的 NodePort + // +optional + NodePortAssigned uint16 `json:"nodePortAssigned"` } // +kubebuilder:object:root=true diff --git a/config/crd/bases/devcontainer.devstar.cn_devcontainerapps.yaml b/config/crd/bases/devcontainer.devstar.cn_devcontainerapps.yaml index dc352e8..41453fa 100644 --- a/config/crd/bases/devcontainer.devstar.cn_devcontainerapps.yaml +++ b/config/crd/bases/devcontainer.devstar.cn_devcontainerapps.yaml @@ -150,6 +150,9 @@ spec: scheduled. format: date-time type: string + nodePortAssigned: + description: NodePortAssigned 存储 DevcontainerApp CRD调度后集群分配的 NodePort + type: integer type: object type: object served: true diff --git a/config/samples/devcontainer_v1_devcontainerapp.yaml b/config/samples/devcontainer_v1_devcontainerapp.yaml index d5f5c67..c60982d 100644 --- a/config/samples/devcontainer_v1_devcontainerapp.yaml +++ b/config/samples/devcontainer_v1_devcontainerapp.yaml @@ -2,7 +2,7 @@ apiVersion: devcontainer.devstar.cn/v1 kind: DevcontainerApp metadata: name: daimingchen-devstar-beef092a69c011ef9c00000c2952a362 - namespace: devstar-devcontainer-ns + namespace: devstar-studio-ns labels: app.kubernetes.io/name: devstar-devcontainer-kubebuilder-scaffold app.kubernetes.io/managed-by: kustomize diff --git a/internal/controller/devcontainerapp_controller.go b/internal/controller/devcontainerapp_controller.go index 686b89a..021c142 100644 --- a/internal/controller/devcontainerapp_controller.go +++ b/internal/controller/devcontainerapp_controller.go @@ -29,7 +29,7 @@ import ( devcontainer_v1 "devstar.cn/DevcontainerApp/api/v1" devcontainer_controller_utils "devstar.cn/DevcontainerApp/internal/controller/utils" - app_v1 "k8s.io/api/apps/v1" + apps_v1 "k8s.io/api/apps/v1" core_v1 "k8s.io/api/core/v1" k8s_sigs_controller_runtime_utils "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) @@ -57,10 +57,11 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ logger := log.FromContext(ctx) var err error - // 1. 读取缓存中的 app + // 1. 读取缓存中的 DevcontainerApp app := &devcontainer_v1.DevcontainerApp{} err = r.Get(ctx, req.NamespacedName, app) if err != nil { + // 当 CRD 资源 “DevcontainerApp” 被删除后,直接返回空结果,跳过剩下步骤 return ctrl.Result{}, client.IgnoreNotFound(err) } @@ -73,7 +74,7 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // 2.2 查找 集群中同名称的 StatefulSet - statefulSetInNamespace := &app_v1.StatefulSet{} + statefulSetInNamespace := &apps_v1.StatefulSet{} err = r.Get(ctx, req.NamespacedName, statefulSetInNamespace) if err != nil { if !errors.IsNotFound(err) { @@ -84,14 +85,15 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ logger.Error(err, "Failed to create StatefulSet") return ctrl.Result{}, err } - } - // 这里会反复触发更新 - // 原因:在 SetupWithManager方法中,监听了 StatefulSet ,所以只要更新 StatefulSet 就会触发 - // 此处更新和 controllerManager 更新 StatefulSet 都会触发更新事件,导致循环触发 - //修复方法:加上判断条件,仅在 app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image 时才更新 StatefulSet - if app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image { - if err := r.Update(ctx, statefulSet); err != nil { - return ctrl.Result{}, err + } else { + // 这里会反复触发更新 + // 原因:在 SetupWithManager方法中,监听了 StatefulSet ,所以只要更新 StatefulSet 就会触发 + // 此处更新和 controllerManager 更新 StatefulSet 都会触发更新事件,导致循环触发 + //修复方法:加上判断条件,仅在 app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image 时才更新 StatefulSet + if app.Spec.StatefulSet.Image != statefulSet.Spec.Template.Spec.Containers[0].Image { + if err := r.Update(ctx, statefulSet); err != nil { + return ctrl.Result{}, err + } } } @@ -100,19 +102,27 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ if err := k8s_sigs_controller_runtime_utils.SetControllerReference(app, service, r.Scheme); err != nil { return ctrl.Result{}, err } - serviceInNamespace := &core_v1.Service{} - err = r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, serviceInNamespace) + serviceInCluster := &core_v1.Service{} + err = r.Get(ctx, types.NamespacedName{Name: app.Name, Namespace: app.Namespace}, serviceInCluster) if err != nil { if !errors.IsNotFound(err) { return ctrl.Result{}, err } err = r.Create(ctx, service) - if err != nil && !errors.IsAlreadyExists(err) { - logger.Error(err, "create service failed") + if err == nil { + // 创建 NodePort Service 成功只执行一次 ==> 将NodePort 端口分配信息更新到 app.Status + logger.Info("[DevStar][DevContainer] NodePort Assigned", "nodePortAssigned", service.Spec.Ports[0].NodePort) + + app.Status.NodePortAssigned = uint16(service.Spec.Ports[0].NodePort) + if err := r.Status().Update(ctx, app); err != nil { + logger.Error(err, "Failed to update NodePort of DevcontainerApp", "nodePortAssigned", service.Spec.Ports[0].NodePort) + return ctrl.Result{}, err + } + } else if !errors.IsAlreadyExists(err) { + logger.Error(err, "Failed to create DevcontainerApp NodePort Service", "nodePortServiceName", service.Name) return ctrl.Result{}, err } } - return ctrl.Result{}, nil } @@ -120,7 +130,7 @@ func (r *DevcontainerAppReconciler) Reconcile(ctx context.Context, req ctrl.Requ func (r *DevcontainerAppReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&devcontainer_v1.DevcontainerApp{}). - Owns(&app_v1.StatefulSet{}). + Owns(&apps_v1.StatefulSet{}). Owns(&core_v1.Service{}). Complete(r) } diff --git a/internal/controller/templates/service.yaml b/internal/controller/templates/service.yaml index 046dabe..973ce89 100644 --- a/internal/controller/templates/service.yaml +++ b/internal/controller/templates/service.yaml @@ -6,6 +6,7 @@ metadata: spec: selector: app: {{.ObjectMeta.Name}} + devstar-resource-type: devstar-devcontainer sessionAffinity: None type: NodePort externalTrafficPolicy: Cluster diff --git a/internal/controller/templates/statefulset.yaml b/internal/controller/templates/statefulset.yaml index f4ee4b1..cd99347 100644 --- a/internal/controller/templates/statefulset.yaml +++ b/internal/controller/templates/statefulset.yaml @@ -5,16 +5,19 @@ metadata: namespace: {{.ObjectMeta.Namespace}} labels: app: {{.ObjectMeta.Name}} + devstar-resource-type: devstar-devcontainer spec: podManagementPolicy: OrderedReady replicas: 1 selector: matchLabels: app: {{.ObjectMeta.Name}} + devstar-resource-type: devstar-devcontainer template: metadata: labels: app: {{.ObjectMeta.Name}} + devstar-resource-type: devstar-devcontainer spec: containers: - name: {{.ObjectMeta.Name}}