176 lines
6.9 KiB
Go
176 lines
6.9 KiB
Go
|
/*
|
|||
|
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), ®istry)
|
|||
|
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, ®istry, 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
|
|||
|
}
|