@@ -494,6 +494,192 @@ func (s *ServiceIdResolverTestSuite) TestServiceIdentityToPodLabelsForWorkloadSe
494494 s .Require ().Len (pods , 0 )
495495}
496496
497+ func (s * ServiceIdResolverTestSuite ) TestServiceIdentityToPodLabelsForWorkloadSelection_ServiceKind_ExternalNameService () {
498+ serviceName := "external-service"
499+ namespace := "cool-namespace"
500+ service := serviceidentity.ServiceIdentity {Name : serviceName , Namespace : namespace , Kind : serviceidentity .KindService }
501+
502+ // ExternalName services have no selector and should be handled gracefully
503+ serviceObj := corev1.Service {
504+ ObjectMeta : metav1.ObjectMeta {Name : serviceName , Namespace : namespace },
505+ Spec : corev1.ServiceSpec {
506+ Type : corev1 .ServiceTypeExternalName ,
507+ ExternalName : "external.example.com" ,
508+ Selector : nil , // ExternalName services have no selector
509+ },
510+ }
511+ s .Client .EXPECT ().Get (gomock .Any (), types.NamespacedName {Name : serviceName , Namespace : namespace }, & corev1.Service {}).Do (func (_ context.Context , name types.NamespacedName , svc * corev1.Service , _ ... any ) {
512+ serviceObj .DeepCopyInto (svc )
513+ })
514+
515+ // Should return no pods with ok=false and no error for ExternalName services
516+ pods , ok , err := s .Resolver .ResolveServiceIdentityToPodSlice (context .Background (), service )
517+ s .Require ().NoError (err )
518+ s .Require ().False (ok )
519+ s .Require ().Len (pods , 0 )
520+ }
521+
522+ func (s * ServiceIdResolverTestSuite ) TestResolvePodToServiceIdentity_WorkflowOwner () {
523+ podName := "workflow-pod-12345"
524+ podNamespace := "argo-namespace"
525+ workflowName := "my-workflow"
526+
527+ myPod := corev1.Pod {
528+ ObjectMeta : metav1.ObjectMeta {
529+ Name : podName ,
530+ Namespace : podNamespace ,
531+ OwnerReferences : []metav1.OwnerReference {
532+ {
533+ Kind : "Workflow" ,
534+ Name : workflowName ,
535+ APIVersion : "argoproj.io/v1alpha1" ,
536+ },
537+ },
538+ },
539+ }
540+
541+ workflowAsObject := unstructured.Unstructured {}
542+ workflowAsObject .SetName (workflowName )
543+ workflowAsObject .SetNamespace (podNamespace )
544+ workflowAsObject .SetKind ("Workflow" )
545+ workflowAsObject .SetAPIVersion ("argoproj.io/v1alpha1" )
546+
547+ emptyObject := & unstructured.Unstructured {}
548+ emptyObject .SetKind ("Workflow" )
549+ emptyObject .SetAPIVersion ("argoproj.io/v1alpha1" )
550+ s .Client .EXPECT ().Get (gomock .Any (), types.NamespacedName {Name : workflowName , Namespace : podNamespace }, emptyObject ).Do (
551+ func (_ context.Context , _ types.NamespacedName , obj * unstructured.Unstructured , _ ... any ) error {
552+ workflowAsObject .DeepCopyInto (obj )
553+ return nil
554+ })
555+
556+ service , err := s .Resolver .ResolvePodToServiceIdentity (context .Background (), & myPod )
557+ s .Require ().NoError (err )
558+ s .Require ().Equal ("argo-workflow" , service .Name )
559+ s .Require ().Equal ("Workflow" , service .Kind )
560+ }
561+
562+ func (s * ServiceIdResolverTestSuite ) TestResolvePodToServiceIdentity_SparkApplicationOwner () {
563+ podName := "spark-pod-12345"
564+ podNamespace := "spark-namespace"
565+ sparkAppName := "my-spark-app"
566+
567+ myPod := corev1.Pod {
568+ ObjectMeta : metav1.ObjectMeta {
569+ Name : podName ,
570+ Namespace : podNamespace ,
571+ OwnerReferences : []metav1.OwnerReference {
572+ {
573+ Kind : "SparkApplication" ,
574+ Name : sparkAppName ,
575+ APIVersion : "sparkoperator.k8s.io/v1beta2" ,
576+ },
577+ },
578+ },
579+ }
580+
581+ sparkAsObject := unstructured.Unstructured {}
582+ sparkAsObject .SetName (sparkAppName )
583+ sparkAsObject .SetNamespace (podNamespace )
584+ sparkAsObject .SetKind ("SparkApplication" )
585+ sparkAsObject .SetAPIVersion ("sparkoperator.k8s.io/v1beta2" )
586+
587+ emptyObject := & unstructured.Unstructured {}
588+ emptyObject .SetKind ("SparkApplication" )
589+ emptyObject .SetAPIVersion ("sparkoperator.k8s.io/v1beta2" )
590+ s .Client .EXPECT ().Get (gomock .Any (), types.NamespacedName {Name : sparkAppName , Namespace : podNamespace }, emptyObject ).Do (
591+ func (_ context.Context , _ types.NamespacedName , obj * unstructured.Unstructured , _ ... any ) error {
592+ sparkAsObject .DeepCopyInto (obj )
593+ return nil
594+ })
595+
596+ service , err := s .Resolver .ResolvePodToServiceIdentity (context .Background (), & myPod )
597+ s .Require ().NoError (err )
598+ s .Require ().Equal ("spark" , service .Name )
599+ s .Require ().Equal ("SparkApplication" , service .Kind )
600+ }
601+
602+ func (s * ServiceIdResolverTestSuite ) TestResolvePodToServiceIdentity_RunnerDeploymentOwner () {
603+ podName := "runner-pod-12345"
604+ podNamespace := "actions-namespace"
605+ runnerDeploymentName := "my-runner-deployment"
606+
607+ myPod := corev1.Pod {
608+ ObjectMeta : metav1.ObjectMeta {
609+ Name : podName ,
610+ Namespace : podNamespace ,
611+ OwnerReferences : []metav1.OwnerReference {
612+ {
613+ Kind : "RunnerDeployment" ,
614+ Name : runnerDeploymentName ,
615+ APIVersion : "actions.summerwind.dev/v1alpha1" ,
616+ },
617+ },
618+ },
619+ }
620+
621+ runnerAsObject := unstructured.Unstructured {}
622+ runnerAsObject .SetName (runnerDeploymentName )
623+ runnerAsObject .SetNamespace (podNamespace )
624+ runnerAsObject .SetKind ("RunnerDeployment" )
625+ runnerAsObject .SetAPIVersion ("actions.summerwind.dev/v1alpha1" )
626+
627+ emptyObject := & unstructured.Unstructured {}
628+ emptyObject .SetKind ("RunnerDeployment" )
629+ emptyObject .SetAPIVersion ("actions.summerwind.dev/v1alpha1" )
630+ s .Client .EXPECT ().Get (gomock .Any (), types.NamespacedName {Name : runnerDeploymentName , Namespace : podNamespace }, emptyObject ).Do (
631+ func (_ context.Context , _ types.NamespacedName , obj * unstructured.Unstructured , _ ... any ) error {
632+ runnerAsObject .DeepCopyInto (obj )
633+ return nil
634+ })
635+
636+ service , err := s .Resolver .ResolvePodToServiceIdentity (context .Background (), & myPod )
637+ s .Require ().NoError (err )
638+ s .Require ().Equal ("actions-runner" , service .Name )
639+ s .Require ().Equal ("RunnerDeployment" , service .Kind )
640+ }
641+
642+ func (s * ServiceIdResolverTestSuite ) TestResolvePodToServiceIdentity_ServiceOwner () {
643+ podName := "service-pod-12345"
644+ podNamespace := "service-namespace"
645+ serviceName := "my-service"
646+
647+ myPod := corev1.Pod {
648+ ObjectMeta : metav1.ObjectMeta {
649+ Name : podName ,
650+ Namespace : podNamespace ,
651+ OwnerReferences : []metav1.OwnerReference {
652+ {
653+ Kind : "Service" ,
654+ Name : serviceName ,
655+ APIVersion : "v1" ,
656+ },
657+ },
658+ },
659+ }
660+
661+ serviceAsObject := unstructured.Unstructured {}
662+ serviceAsObject .SetName (serviceName )
663+ serviceAsObject .SetNamespace (podNamespace )
664+ serviceAsObject .SetKind ("Service" )
665+ serviceAsObject .SetAPIVersion ("v1" )
666+
667+ emptyObject := & unstructured.Unstructured {}
668+ emptyObject .SetKind ("Service" )
669+ emptyObject .SetAPIVersion ("v1" )
670+ s .Client .EXPECT ().Get (gomock .Any (), types.NamespacedName {Name : serviceName , Namespace : podNamespace }, emptyObject ).Do (
671+ func (_ context.Context , _ types.NamespacedName , obj * unstructured.Unstructured , _ ... any ) error {
672+ serviceAsObject .DeepCopyInto (obj )
673+ return nil
674+ })
675+
676+ service , err := s .Resolver .ResolvePodToServiceIdentity (context .Background (), & myPod )
677+ s .Require ().NoError (err )
678+ s .Require ().Equal (serviceName , service .Name )
679+ // Service kind should include API group to disambiguate from core Service
680+ s .Require ().Equal ("Service." , service .Kind )
681+ }
682+
497683func (s * ServiceIdResolverTestSuite ) TestUserSpecifiedAnnotationForServiceName () {
498684 annotationName := "coolAnnotationName"
499685 expectedEnvVarName := "OTTERIZE_SERVICE_NAME_OVERRIDE_ANNOTATION"
0 commit comments