pkg/util/platform/platform.go (173 lines of code) (raw):

package platform import ( "context" "errors" "fmt" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" cdPipeApi "github.com/epam/edp-cd-pipeline-operator/v2/pkg/apis/edp/v1" jenkinsApi "github.com/epam/edp-jenkins-operator/v2/pkg/apis/v2/v1" "github.com/epam/edp-jenkins-operator/v2/pkg/util/consts" ) var plog = ctrl.Log.WithName("platform_util") func GetStageInstanceOwner(c client.Client, jj *jenkinsApi.JenkinsJob) (*cdPipeApi.Stage, error) { plog.V(2).Info("start getting stage owner cr", "stage", jj.Name) if ow := GetOwnerReference(consts.StageKind, jj.GetOwnerReferences()); ow != nil { plog.V(2).Info("trying to fetch stage owner from reference", "stage", ow.Name) return GetStageInstance(c, ow.Name, jj.Namespace) } if jj.Spec.StageName != nil { plog.V(2).Info("trying to fetch stage owner from spec", "stage", jj.Spec.StageName) return GetStageInstance(c, *jj.Spec.StageName, jj.Namespace) } return nil, fmt.Errorf("failed to find stage owner for jenkins job %v", jj.Name) } func GetOwnerReference(ownerKind string, ors []metav1.OwnerReference) *metav1.OwnerReference { plog.V(2).Info("finding owner", "kind", ownerKind) if len(ors) == 0 { return nil } for _, o := range ors { if o.Kind == ownerKind { return &o } } return nil } func GetStageInstance(c client.Client, name, namespace string) (*cdPipeApi.Stage, error) { nsn := types.NamespacedName{ Namespace: namespace, Name: name, } i := &cdPipeApi.Stage{} if err := c.Get(context.TODO(), nsn, i); err != nil { return nil, fmt.Errorf("failed to get instance by name %v: %w", name, err) } return i, nil } func GetJenkinsInstanceOwner(c client.Client, name, namespace string, ownerName *string, ors []metav1.OwnerReference, ) (*jenkinsApi.Jenkins, error) { plog.V(2).Info("start getting jenkins owner", "owner name", name) if ow := GetOwnerReference(consts.JenkinsKind, ors); ow != nil { plog.V(2).Info("trying to fetch jenkins owner from reference", "jenkins name", ow.Name) return GetJenkinsInstance(c, ow.Name, namespace) } if ownerName != nil { plog.V(2).Info("trying to fetch jenkins owner from spec", "jenkins name", ownerName) return GetJenkinsInstance(c, *ownerName, namespace) } plog.V(2).Info("trying to fetch first jenkins instance", "namespace", namespace) j, err := GetFirstJenkinsInstance(c, namespace) if err != nil { return nil, err } return j, nil } func GetJenkinsInstance(c client.Client, name, namespace string) (*jenkinsApi.Jenkins, error) { nsn := types.NamespacedName{ Namespace: namespace, Name: name, } instance := &jenkinsApi.Jenkins{} if err := c.Get(context.TODO(), nsn, instance); err != nil { return nil, fmt.Errorf("failed to get jenkins instance by name %v: %w", name, err) } return instance, nil } func GetFirstJenkinsInstance(c client.Client, namespace string) (*jenkinsApi.Jenkins, error) { list := &jenkinsApi.JenkinsList{} if err := c.List(context.TODO(), list, &client.ListOptions{Namespace: namespace}); err != nil { return nil, fmt.Errorf("failed to get Jenkins instances in namespace %v: %w", namespace, err) } if len(list.Items) == 0 { return nil, errors.New("at least one Jenkins instance should be accessible") } j := list.Items[0] return GetJenkinsInstance(c, j.Name, j.Namespace) } func GetJenkinsFolderInstance(k8sClient client.Client, name, namespace string) (*jenkinsApi.JenkinsFolder, error) { namespacedName := types.NamespacedName{ Namespace: namespace, Name: name, } jenkinsFolder := &jenkinsApi.JenkinsFolder{} if err := k8sClient.Get(context.TODO(), namespacedName, jenkinsFolder); err != nil { return nil, fmt.Errorf("failed to get jenkins folder: %w", err) } return jenkinsFolder, nil } // SetControllerReference sets owner as a Controller OwnerReference on owned. // This is used for garbage collection of the owned object and for // reconciling the owner object on changes to owned (with a Watch + EnqueueRequestForOwner). // Since only one OwnerReference can be a controller, it returns an error if // there is another OwnerReference with Controller flag set. func SetControllerReference(owner, object metav1.Object, scheme *runtime.Scheme, isController bool) error { runtimeObject, ok := owner.(runtime.Object) if !ok { return fmt.Errorf("failed to call SetControllerReference: owner of type %T, should be of type runtime.Object", owner) } groupVersionKind, err := apiutil.GVKForObject(runtimeObject, scheme) if err != nil { return fmt.Errorf("failed to get Group Version Kind for object: %w", err) } // Create a new ref ref := *newControllerRef( owner, schema.GroupVersionKind{ Group: groupVersionKind.Group, Version: groupVersionKind.Version, Kind: groupVersionKind.Kind, }, isController, ) existingRefs := object.GetOwnerReferences() foundIndex := -1 for i := 0; i < len(existingRefs); i++ { if referSameObject(&ref, &existingRefs[i]) { foundIndex = i break } if existingRefs[i].Controller != nil && *existingRefs[i].Controller { return newAlreadyOwnedError(object, &existingRefs[i]) } } if foundIndex == -1 { existingRefs = append(existingRefs, ref) } else { existingRefs[foundIndex] = ref } // Update owner references object.SetOwnerReferences(existingRefs) return nil } func newControllerRef(owner metav1.Object, gvk schema.GroupVersionKind, isController bool) *metav1.OwnerReference { blockOwnerDeletion := true or := &metav1.OwnerReference{ APIVersion: gvk.GroupVersion().String(), Kind: gvk.Kind, Name: owner.GetName(), UID: owner.GetUID(), BlockOwnerDeletion: &blockOwnerDeletion, } if isController { or.Controller = &isController } return or } // Returns true if a and b point to the same object. func referSameObject(a, b *metav1.OwnerReference) bool { aGV, err := schema.ParseGroupVersion(a.APIVersion) if err != nil { return false } bGV, err := schema.ParseGroupVersion(b.APIVersion) if err != nil { return false } return aGV == bGV && a.Kind == b.Kind && a.Name == b.Name } func newAlreadyOwnedError(object metav1.Object, owner *metav1.OwnerReference) *controllerutil.AlreadyOwnedError { return &controllerutil.AlreadyOwnedError{ Object: object, Owner: *owner, } }