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
|
||
}
|