zelda/backend/internal/controller/zbuilder_controller.go

176 lines
6.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Copyright 2023 ycyxuehan.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
"github.com/pkg/errors"
zeldaiov1alpha1 "github.com/ycyxuehan/zelda/api/v1alpha1"
"github.com/ycyxuehan/zelda/pkg/config"
"github.com/ycyxuehan/zelda/pkg/utils"
watchers "github.com/ycyxuehan/zelda/watcher"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ZBuilderReconciler reconciles a ZBuilder object
type ZBuilderReconciler struct {
client.Client
Scheme *runtime.Scheme
Config config.Config
}
//+kubebuilder:rbac:groups=zelda.io,resources=zbuilders,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=zelda.io,resources=zbuilders/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=zelda.io,resources=zbuilders/finalizers,verbs=update
//+kubebuilder:rbac:groups=zelda.io,resources=zservices,verbs=get;list;update;patch
//+kubebuilder:rbac:groups=zelda.io,resources=zservices/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=*,resources=pods,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=*,resources=pods/status,verbs=get;update;patch
// Reconcile is part of the main kubernetes reconciliation loop which aims to
// move the current state of the cluster closer to the desired state.
// TODO(user): Modify the Reconcile function to compare the state specified by
// the ZBuilder object against the actual cluster state, and then
// perform operations to make the cluster state reflect the state specified by
// the user.
//
// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.10.0/pkg/reconcile
func (r *ZBuilderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
_ = log.FromContext(ctx)
// TODO(user): your logic here
zbuilder := zeldaiov1alpha1.ZBuilder{}
err := r.Get(ctx, req.NamespacedName, &zbuilder)
if err != nil {
log.Log.Error(err, "cannot get resource zbuilders.zelda.io/%s/%s", req.Namespace, req.Name)
return ctrl.Result{}, err
}
project := zeldaiov1alpha1.ZProject{}
err = r.Get(ctx, utils.NamespacedNameFromObjectReference(zbuilder.Spec.Project), &project)
if err != nil {
log.Log.Error(err, "cannot get resource zprojects.zelda.io/%s/%s", zbuilder.Spec.Project.Namespace, zbuilder.Spec.Project.Name)
return ctrl.Result{}, err
}
//非删除
if zbuilder.GetDeletionTimestamp() == nil || zbuilder.GetDeletionTimestamp().IsZero() {
if zbuilder.Status.Phase == "" {
err = r.DoCreate(ctx, &zbuilder, &project)
if err != nil {
log.Log.Error(err, "do create builder failed")
}
return ctrl.Result{}, err
}
err = r.DoUpdate(ctx, &zbuilder, &project)
if err != nil {
log.Log.Error(err, "update builder failed")
}
}
return ctrl.Result{}, nil
}
// SetupWithManager sets up the controller with the Manager.
func (r *ZBuilderReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&zeldaiov1alpha1.ZBuilder{}).
Watches(&corev1.Pod{},&watchers.ZBuilderWatcher{Client: mgr.GetClient(),}).
Complete(r)
}
//处理update事件 更新状态
func (r *ZBuilderReconciler) DoUpdate(ctx context.Context, zbuilder *zeldaiov1alpha1.ZBuilder, project *zeldaiov1alpha1.ZProject) error {
switch zbuilder.Status.Phase {
case zeldaiov1alpha1.PhaseCompleted: //已完成
return nil
case zeldaiov1alpha1.PhaseDeploy: //部署阶段
return nil
default: //编译打包阶段
if metav1.Now().Sub(zbuilder.Status.CreatedAt.Time) >= r.Config.Timeout {
pod := corev1.Pod{}
err := r.Get(context.Background(), client.ObjectKeyFromObject(zbuilder), &pod)
if err != nil {
return errors.WithMessage(err, "cannot get pod, maybe it create failed")
}
zbuilder.Status = zeldaiov1alpha1.NewZbuilderStatus(&pod.Status)
} else { //超时
zbuilder.Status.Phase = zeldaiov1alpha1.PhaseCompleted
zbuilder.Status.Reason = "TimeOut"
now := metav1.Now()
zbuilder.Status.FinishedAt = &now
}
r.Status().Update(context.Background(), zbuilder)
}
return nil
}
//处理create事件 创建pod开始编译打包过程。
func (r *ZBuilderReconciler) DoCreate(ctx context.Context, zbuilder *zeldaiov1alpha1.ZBuilder, project *zeldaiov1alpha1.ZProject) error {
buildTemplate := zeldaiov1alpha1.ZBuildTemplate{}
registry := zeldaiov1alpha1.ZRegistry{}
err := r.Get(context.Background(), utils.NamespacedNameFromObjectReference(project.Spec.Registry), &registry)
if err != nil {
return errors.WithMessagef(err, "get zregistries.zelda.io/%s/%s failed", project.Spec.Registry.Namespace, project.Spec.Registry.Name)
}
err = r.ProcessRegistrySecret(ctx, &registry, zbuilder)
if err != nil {
return errors.WithMessagef(err, "process the secret of registry failed")
}
subProject := project.SubProject(zbuilder.Spec.SubProject)
err = r.Get(context.Background(), utils.NamespacedNameFromObjectReference(subProject.BuildSpec.Template), &buildTemplate)
if err != nil {
return errors.WithMessagef(err, "get zbuildtemplates.zelda.io/%s/%s failed", subProject.BuildSpec.Template.Namespace, subProject.BuildSpec.Template.Name)
}
pod := zbuilder.Pod(buildTemplate.Spec.DeepCopy(), r.Config.GitImage, project.Spec.Git.Repo, subProject, registry.DeepCopy())
//先更新状态再创建pod避免创建pod触发状态更新导致冲突
//更新状态
zbuilder.Status.Phase = zeldaiov1alpha1.PhaseCreating
err = r.Status().Update(context.Background(), zbuilder)
if err != nil {
return errors.WithMessage(err, "update builder status failed")
}
//创建pod
err = r.Create(context.Background(), &pod, &client.CreateOptions{})
return errors.WithMessagef(err, "create pods/%s/%s failed", pod.GetNamespace(), pod.GetName())
}
// //UpdateStatus 更新状态
// func (r *ZBuilderReconciler)UpdateStatus(zbuilder*zeldaiov1alpha1.ZBuilder, pod *corev1.Pod)error{
// return nil
// }
func (r *ZBuilderReconciler) ProcessRegistrySecret(ctx context.Context, zregistry *zeldaiov1alpha1.ZRegistry, zbuilder *zeldaiov1alpha1.ZBuilder) error {
secret := &corev1.Secret{}
err := r.Get(ctx, client.ObjectKey{Namespace: zbuilder.GetNamespace(), Name: zregistry.SecreteName()}, secret)
if err == nil {
return nil
}
secret = zregistry.NamespacedSecret(zbuilder.GetNamespace())
err = r.Create(ctx, secret, &client.CreateOptions{})
return err
}