github.com/kubeflow/training-operator@v1.7.0/pkg/controller.v1/control/service_control.go (about)

     1  // Copyright 2019 The Kubeflow Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package control
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"sync"
    21  
    22  	log "github.com/sirupsen/logrus"
    23  	"k8s.io/api/core/v1"
    24  	"k8s.io/apimachinery/pkg/api/errors"
    25  	"k8s.io/apimachinery/pkg/api/meta"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/labels"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	clientset "k8s.io/client-go/kubernetes"
    31  	"k8s.io/client-go/tools/record"
    32  )
    33  
    34  const (
    35  	// FailedCreateServiceReason is added in an event and in a job controller condition
    36  	// when a service for a job is failed to be created.
    37  	FailedCreateServiceReason = "FailedCreateService"
    38  	// SuccessfulCreateServiceReason is added in an event when a service for a job
    39  	// is successfully created.
    40  	SuccessfulCreateServiceReason = "SuccessfulCreateService"
    41  	// FailedDeleteServiceReason is added in an event and in a job condition
    42  	// when a service for a job is failed to be deleted.
    43  	FailedDeleteServiceReason = "FailedDeleteService"
    44  	// SuccessfulDeleteServiceReason is added in an event when a service for a job
    45  	// is successfully deleted.
    46  	SuccessfulDeleteServiceReason = "SuccessfulDeleteService"
    47  )
    48  
    49  // ServiceControlInterface is an interface that knows how to add or delete Services
    50  // created as an interface to allow testing.
    51  type ServiceControlInterface interface {
    52  	// CreateServices creates new Services according to the spec.
    53  	CreateServices(namespace string, service *v1.Service, object runtime.Object) error
    54  	// CreateServicesWithControllerRef creates new services according to the spec, and sets object as the service's controller.
    55  	CreateServicesWithControllerRef(namespace string, service *v1.Service, object runtime.Object, controllerRef *metav1.OwnerReference) error
    56  	// PatchService patches the service.
    57  	PatchService(namespace, name string, data []byte) error
    58  	// DeleteService deletes the service identified by serviceID.
    59  	DeleteService(namespace, serviceID string, object runtime.Object) error
    60  }
    61  
    62  // RealServiceControl is the default implementation of ServiceControlInterface.
    63  type RealServiceControl struct {
    64  	KubeClient clientset.Interface
    65  	Recorder   record.EventRecorder
    66  }
    67  
    68  func (r RealServiceControl) PatchService(namespace, name string, data []byte) error {
    69  	_, err := r.KubeClient.CoreV1().Services(namespace).Patch(context.TODO(), name, types.StrategicMergePatchType, data, metav1.PatchOptions{})
    70  	return err
    71  }
    72  
    73  func (r RealServiceControl) CreateServices(namespace string, service *v1.Service, object runtime.Object) error {
    74  	return r.createServices(namespace, service, object, nil)
    75  }
    76  
    77  func (r RealServiceControl) CreateServicesWithControllerRef(namespace string, service *v1.Service, controllerObject runtime.Object, controllerRef *metav1.OwnerReference) error {
    78  	if err := ValidateControllerRef(controllerRef); err != nil {
    79  		return err
    80  	}
    81  	return r.createServices(namespace, service, controllerObject, controllerRef)
    82  }
    83  
    84  func (r RealServiceControl) createServices(namespace string, service *v1.Service, object runtime.Object, controllerRef *metav1.OwnerReference) error {
    85  	if labels.Set(service.Labels).AsSelectorPreValidated().Empty() {
    86  		return fmt.Errorf("unable to create Services, no labels")
    87  	}
    88  	serviceWithOwner, err := GetServiceFromTemplate(service, object, controllerRef)
    89  	if err != nil {
    90  		r.Recorder.Eventf(object, v1.EventTypeWarning, FailedCreateServiceReason, "Error creating: %v", err)
    91  		return fmt.Errorf("unable to create services: %v", err)
    92  	}
    93  
    94  	newService, err := r.KubeClient.CoreV1().Services(namespace).Create(context.TODO(), serviceWithOwner, metav1.CreateOptions{})
    95  	if err != nil {
    96  		r.Recorder.Eventf(object, v1.EventTypeWarning, FailedCreateServiceReason, "Error creating: %v", err)
    97  		return fmt.Errorf("unable to create services: %v", err)
    98  	}
    99  
   100  	accessor, err := meta.Accessor(object)
   101  	if err != nil {
   102  		log.Errorf("parentObject does not have ObjectMeta, %v", err)
   103  		return nil
   104  	}
   105  	log.Infof("Controller %v created service %v", accessor.GetName(), newService.Name)
   106  	r.Recorder.Eventf(object, v1.EventTypeNormal, SuccessfulCreateServiceReason, "Created service: %v", newService.Name)
   107  
   108  	return nil
   109  }
   110  
   111  // DeleteService deletes the service identified by serviceID.
   112  func (r RealServiceControl) DeleteService(namespace, serviceID string, object runtime.Object) error {
   113  	accessor, err := meta.Accessor(object)
   114  	if err != nil {
   115  		return fmt.Errorf("object does not have ObjectMeta, %v", err)
   116  	}
   117  	service, err := r.KubeClient.CoreV1().Services(namespace).Get(context.TODO(), serviceID, metav1.GetOptions{})
   118  	if err != nil {
   119  		if errors.IsNotFound(err) {
   120  			return nil
   121  		}
   122  		return err
   123  	}
   124  	if service.DeletionTimestamp != nil {
   125  		log.Infof("service %s/%s is terminating, skip deleting", service.Namespace, service.Name)
   126  		return nil
   127  	}
   128  	log.Infof("Controller %v deleting service %v/%v", accessor.GetName(), namespace, serviceID)
   129  	if err := r.KubeClient.CoreV1().Services(namespace).Delete(context.TODO(), serviceID, metav1.DeleteOptions{}); err != nil {
   130  		r.Recorder.Eventf(object, v1.EventTypeWarning, FailedDeleteServiceReason, "Error deleting: %v", err)
   131  		return fmt.Errorf("unable to delete service: %v", err)
   132  	} else {
   133  		r.Recorder.Eventf(object, v1.EventTypeNormal, SuccessfulDeleteServiceReason, "Deleted service: %v", serviceID)
   134  	}
   135  	return nil
   136  }
   137  
   138  type FakeServiceControl struct {
   139  	sync.Mutex
   140  	Templates         []v1.Service
   141  	ControllerRefs    []metav1.OwnerReference
   142  	DeleteServiceName []string
   143  	Patches           [][]byte
   144  	Err               error
   145  	CreateLimit       int
   146  	CreateCallCount   int
   147  }
   148  
   149  var _ ServiceControlInterface = &FakeServiceControl{}
   150  
   151  func (f *FakeServiceControl) PatchService(namespace, name string, data []byte) error {
   152  	f.Lock()
   153  	defer f.Unlock()
   154  	f.Patches = append(f.Patches, data)
   155  	if f.Err != nil {
   156  		return f.Err
   157  	}
   158  	return nil
   159  }
   160  
   161  func (f *FakeServiceControl) CreateServices(namespace string, service *v1.Service, object runtime.Object) error {
   162  	f.Lock()
   163  	defer f.Unlock()
   164  	f.CreateCallCount++
   165  	if f.CreateLimit != 0 && f.CreateCallCount > f.CreateLimit {
   166  		return fmt.Errorf("not creating service, limit %d already reached (create call %d)", f.CreateLimit, f.CreateCallCount)
   167  	}
   168  	f.Templates = append(f.Templates, *service)
   169  	if f.Err != nil {
   170  		return f.Err
   171  	}
   172  	return nil
   173  }
   174  
   175  func (f *FakeServiceControl) CreateServicesWithControllerRef(namespace string, service *v1.Service, object runtime.Object, controllerRef *metav1.OwnerReference) error {
   176  	f.Lock()
   177  	defer f.Unlock()
   178  	f.CreateCallCount++
   179  	if f.CreateLimit != 0 && f.CreateCallCount > f.CreateLimit {
   180  		return fmt.Errorf("not creating service, limit %d already reached (create call %d)", f.CreateLimit, f.CreateCallCount)
   181  	}
   182  	f.Templates = append(f.Templates, *service)
   183  	f.ControllerRefs = append(f.ControllerRefs, *controllerRef)
   184  	if f.Err != nil {
   185  		return f.Err
   186  	}
   187  	return nil
   188  }
   189  
   190  func (f *FakeServiceControl) DeleteService(namespace string, serviceID string, object runtime.Object) error {
   191  	f.Lock()
   192  	defer f.Unlock()
   193  	f.DeleteServiceName = append(f.DeleteServiceName, serviceID)
   194  	if f.Err != nil {
   195  		return f.Err
   196  	}
   197  	return nil
   198  }
   199  
   200  func (f *FakeServiceControl) Clear() {
   201  	f.Lock()
   202  	defer f.Unlock()
   203  	f.DeleteServiceName = []string{}
   204  	f.Templates = []v1.Service{}
   205  	f.ControllerRefs = []metav1.OwnerReference{}
   206  	f.Patches = [][]byte{}
   207  	f.CreateLimit = 0
   208  	f.CreateCallCount = 0
   209  }