diff --git a/operator/apis/machinelearning/v1/seldondeployment_types.go b/operator/apis/machinelearning/v1/seldondeployment_types.go index 43580ad274..392d20cf82 100644 --- a/operator/apis/machinelearning/v1/seldondeployment_types.go +++ b/operator/apis/machinelearning/v1/seldondeployment_types.go @@ -182,9 +182,9 @@ func cleanContainerName(name string) string { return re.ReplaceAllString(strings.ToLower(name), "-") } -func GetContainerServiceName(mlDep *SeldonDeployment, predictorSpec PredictorSpec, c *v1.Container) string { +func GetContainerServiceName(spec *SeldonDeploymentSpec, predictorSpec PredictorSpec, c *v1.Container) string { containerImageName := cleanContainerName(c.Image) - svcName := mlDep.Spec.Name + "-" + predictorSpec.Name + "-" + c.Name + svcName := spec.Name + "-" + predictorSpec.Name + "-" + c.Name if containerImageName != "" { svcName = svcName + "-" + containerImageName } diff --git a/operator/apis/machinelearning/v1/seldondeployment_webhook.go b/operator/apis/machinelearning/v1/seldondeployment_webhook.go index e88409a076..9b47cc4070 100644 --- a/operator/apis/machinelearning/v1/seldondeployment_webhook.go +++ b/operator/apis/machinelearning/v1/seldondeployment_webhook.go @@ -179,7 +179,7 @@ func getUpdatePortNumMap(name string, nextPortNum *int32, portMap map[string]int return portMap[name] } -func (r *SeldonDeployment) DefaultSeldonDeployment() { +func (r *SeldonDeploymentSpec) DefaultSeldonDeployment(namespace string) { var firstPuPortNum int32 = 9000 if env_preditive_unit_service_port, ok := os.LookupEnv("PREDICTIVE_UNIT_SERVICE_PORT"); ok { @@ -194,12 +194,8 @@ func (r *SeldonDeployment) DefaultSeldonDeployment() { portMap := map[string]int32{} - if r.ObjectMeta.Namespace == "" { - r.ObjectMeta.Namespace = "default" - } - - for i := 0; i < len(r.Spec.Predictors); i++ { - p := r.Spec.Predictors[i] + for i := 0; i < len(r.Predictors); i++ { + p := r.Predictors[i] if p.Graph.Type == nil { ty := UNKNOWN_TYPE p.Graph.Type = &ty @@ -213,10 +209,10 @@ func (r *SeldonDeployment) DefaultSeldonDeployment() { } addDefaultsToGraph(p.Graph) - r.Spec.Predictors[i] = p + r.Predictors[i] = p for j := 0; j < len(p.ComponentSpecs); j++ { - cSpec := r.Spec.Predictors[i].ComponentSpecs[j] + cSpec := r.Predictors[i].ComponentSpecs[j] // add service details for each container - looping this way as if containers in same pod and its the engine pod both need to be localhost for k := 0; k < len(cSpec.Spec.Containers); k++ { @@ -262,11 +258,11 @@ func (r *SeldonDeployment) DefaultSeldonDeployment() { // Set ports and hostname in predictive unit so engine can read it from SDep // if this is the first componentSpec then it's the one to put the engine in - note using outer loop counter here - if _, hasSeparateEnginePod := r.Spec.Annotations[ANNOTATION_SEPARATE_ENGINE]; j == 0 && !hasSeparateEnginePod { + if _, hasSeparateEnginePod := r.Annotations[ANNOTATION_SEPARATE_ENGINE]; j == 0 && !hasSeparateEnginePod { pu.Endpoint.ServiceHost = "localhost" } else { containerServiceValue := GetContainerServiceName(r, p, con) - pu.Endpoint.ServiceHost = containerServiceValue + "." + r.ObjectMeta.Namespace + ".svc.cluster.local." + pu.Endpoint.ServiceHost = containerServiceValue + "." + namespace + ".svc.cluster.local." } pu.Endpoint.ServicePort = portNum } @@ -325,7 +321,7 @@ func (r *SeldonDeployment) DefaultSeldonDeployment() { p.ComponentSpecs = []*SeldonPodSpec{&podSpec} // p is a copy so update the entry - r.Spec.Predictors[i] = p + r.Predictors[i] = p } } @@ -354,11 +350,11 @@ func (r *SeldonDeployment) DefaultSeldonDeployment() { // Set ports and hostname in predictive unit so engine can read it from SDep // if this is the firstPuPortNum then we've not added engine yet so put the engine in here if pu.Endpoint.ServiceHost == "" { - if _, hasSeparateEnginePod := r.Spec.Annotations[ANNOTATION_SEPARATE_ENGINE]; !hasSeparateEnginePod { + if _, hasSeparateEnginePod := r.Annotations[ANNOTATION_SEPARATE_ENGINE]; !hasSeparateEnginePod { pu.Endpoint.ServiceHost = "localhost" } else { containerServiceValue := GetContainerServiceName(r, p, con) - pu.Endpoint.ServiceHost = containerServiceValue + "." + r.ObjectMeta.Namespace + ".svc.cluster.local." + pu.Endpoint.ServiceHost = containerServiceValue + "." + namespace + ".svc.cluster.local." } } if pu.Endpoint.ServicePort == 0 { @@ -417,22 +413,22 @@ func checkPredictiveUnits(pu *PredictiveUnit, p *PredictorSpec, fldPath *field.P return allErrs } -func checkTraffic(mlDep *SeldonDeployment, fldPath *field.Path, allErrs field.ErrorList) field.ErrorList { +func checkTraffic(spec *SeldonDeploymentSpec, fldPath *field.Path, allErrs field.ErrorList) field.ErrorList { var trafficSum int32 = 0 var shadows int = 0 - for i := 0; i < len(mlDep.Spec.Predictors); i++ { - p := mlDep.Spec.Predictors[i] + for i := 0; i < len(spec.Predictors); i++ { + p := spec.Predictors[i] trafficSum = trafficSum + p.Traffic if p.Shadow == true { shadows += 1 } } - if trafficSum != 100 && (len(mlDep.Spec.Predictors)-shadows) > 1 { - allErrs = append(allErrs, field.Invalid(fldPath, mlDep.Name, "Traffic must sum to 100 for multiple predictors")) + if trafficSum != 100 && (len(spec.Predictors)-shadows) > 1 { + allErrs = append(allErrs, field.Invalid(fldPath, spec.Name, "Traffic must sum to 100 for multiple predictors")) } - if trafficSum > 0 && trafficSum < 100 && len(mlDep.Spec.Predictors) == 1 { - allErrs = append(allErrs, field.Invalid(fldPath, mlDep.Name, "Traffic must sum be 100 for a single predictor when set")) + if trafficSum > 0 && trafficSum < 100 && len(spec.Predictors) == 1 { + allErrs = append(allErrs, field.Invalid(fldPath, spec.Name, "Traffic must sum be 100 for a single predictor when set")) } return allErrs @@ -446,11 +442,11 @@ func sizeOfGraph(p *PredictiveUnit) int { return count + 1 } -func (r *SeldonDeployment) validateSeldonDeployment() error { +func (r *SeldonDeploymentSpec) ValidateSeldonDeployment() error { var allErrs field.ErrorList predictorNames := make(map[string]bool) - for i, p := range r.Spec.Predictors { + for i, p := range r.Predictors { _, noEngine := p.Annotations[ANNOTATION_NO_ENGINE] if noEngine && sizeOfGraph(p.Graph) > 1 { @@ -486,9 +482,12 @@ func (r *SeldonDeployment) validateSeldonDeployment() error { // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *SeldonDeployment) Default() { - seldondeploymentlog.Info("Defaulting web hook called", "name", r.Name) + seldondeploymentlog.Info("Defaulting v1 web hook called", "name", r.Name) - r.DefaultSeldonDeployment() + if r.ObjectMeta.Namespace == "" { + r.ObjectMeta.Namespace = "default" + } + r.Spec.DefaultSeldonDeployment(r.ObjectMeta.Namespace) } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. @@ -498,21 +497,21 @@ var _ webhook.Validator = &SeldonDeployment{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateCreate() error { - seldondeploymentlog.Info("Validating Webhook called for CREATE", "name", r.Name) + seldondeploymentlog.Info("Validating v1 Webhook called for CREATE", "name", r.Name) - return r.validateSeldonDeployment() + return r.Spec.ValidateSeldonDeployment() } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateUpdate(old runtime.Object) error { - seldondeploymentlog.Info("Validating webhook called for UPDATE", "name", r.Name) + seldondeploymentlog.Info("Validating v1 webhook called for UPDATE", "name", r.Name) - return r.validateSeldonDeployment() + return r.Spec.ValidateSeldonDeployment() } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateDelete() error { - seldondeploymentlog.Info("Validating webhook called for DELETE", "name", r.Name) + seldondeploymentlog.Info("Validating v1 webhook called for DELETE", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. return nil diff --git a/operator/apis/machinelearning/v1alpha2/seldondeployment_types.go b/operator/apis/machinelearning/v1alpha2/seldondeployment_types.go index 4d324de020..570d84deb6 100644 --- a/operator/apis/machinelearning/v1alpha2/seldondeployment_types.go +++ b/operator/apis/machinelearning/v1alpha2/seldondeployment_types.go @@ -17,349 +17,10 @@ limitations under the License. package v1alpha2 import ( - "crypto/md5" - "encoding/hex" - "regexp" - "strings" - - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta1" - v1 "k8s.io/api/core/v1" + seldonv1 "github.com/seldonio/seldon-core/operator/apis/machinelearning/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -const ( - Label_seldon_id = "seldon-deployment-id" - Label_seldon_app = "seldon-app" - Label_svc_orch = "seldon-deployment-contains-svcorch" - - PODINFO_VOLUME_NAME = "podinfo" - PODINFO_VOLUME_PATH = "/etc/podinfo" - - ENV_PREDICTIVE_UNIT_SERVICE_PORT = "PREDICTIVE_UNIT_SERVICE_PORT" - ENV_PREDICTIVE_UNIT_PARAMETERS = "PREDICTIVE_UNIT_PARAMETERS" - ENV_PREDICTIVE_UNIT_ID = "PREDICTIVE_UNIT_ID" - ENV_PREDICTOR_ID = "PREDICTOR_ID" - ENV_SELDON_DEPLOYMENT_ID = "SELDON_DEPLOYMENT_ID" - - ANNOTATION_JAVA_OPTS = "seldon.io/engine-java-opts" - ANNOTATION_SEPARATE_ENGINE = "seldon.io/engine-separate-pod" - ANNOTATION_HEADLESS_SVC = "seldon.io/headless-svc" - ANNOTATION_NO_ENGINE = "seldon.io/no-engine" - ANNOTATION_CUSTOM_SVC_NAME = "seldon.io/svc-name" -) - -func hash(text string) string { - hasher := md5.New() - hasher.Write([]byte(text)) - return hex.EncodeToString(hasher.Sum(nil)) -} - -func containerHash(podSpec *SeldonPodSpec) string { - s := []string{} - for i := 0; i < len(podSpec.Spec.Containers); i++ { - c := podSpec.Spec.Containers[i] - s = append(s, c.Name) - s = append(s, c.Image) - } - key := strings.Join(s, ":") + ";" - return hash(key)[:7] -} - -func createPredictorHash(p *PredictorSpec) string { - s := []string{} - for i := 0; i < len(p.ComponentSpecs); i++ { - s = append(s, containerHash(p.ComponentSpecs[i])) - } - key := strings.Join(s, ",") + "," - return hash(key)[:7] -} - -func GetSeldonDeploymentName(mlDep *SeldonDeployment) string { - name := mlDep.Spec.Name + "-" + mlDep.ObjectMeta.Name - if len(name) > 63 { - return "seldon-" + hash(name) - } else { - return name - } -} - -func GetExplainerDeploymentName(sdepName string, predictorSpec *PredictorSpec) string { - name := sdepName + "-" + predictorSpec.Name + "-explainer" - if len(name) > 63 { - return "seldon-" + hash(name) - } else { - return name - } -} - -func GetDeploymentName(mlDep *SeldonDeployment, predictorSpec PredictorSpec, podSpec *SeldonPodSpec) string { - if podSpec != nil && len(podSpec.Metadata.Name) != 0 { - return podSpec.Metadata.Name - } else { - name := mlDep.Spec.Name + "-" + predictorSpec.Name - if podSpec != nil { - name = name + "-" + containerHash(podSpec) - } - if len(name) > 63 { - return "seldon-" + hash(name) - } else { - return name - } - } -} - -func GetServiceOrchestratorName(mlDep *SeldonDeployment, p *PredictorSpec) string { - svcOrchName := mlDep.Spec.Name + "-" + p.Name + "-svc-orch" + "-" + createPredictorHash(p) - if len(svcOrchName) > 63 { - return "seldon-" + hash(svcOrchName) - } else { - return svcOrchName - } -} - -func GetPredictorKey(mlDep *SeldonDeployment, p *PredictorSpec) string { - if annotation, hasAnnotation := p.Annotations[ANNOTATION_CUSTOM_SVC_NAME]; hasAnnotation { - return annotation - } else { - return getPredictorKeyAutoGenerated(mlDep, p) - } -} - -func getPredictorKeyAutoGenerated(mlDep *SeldonDeployment, p *PredictorSpec) string { - pName := mlDep.Name + "-" + mlDep.Spec.Name + "-" + p.Name - if len(pName) > 63 { - return "seldon-" + hash(pName) - } else { - return pName - } -} - -func GetPredictorServiceNameKey(c *v1.Container) string { - return Label_seldon_app + "-" + c.Name -} - -func GetPredictiveUnit(pu *PredictiveUnit, name string) *PredictiveUnit { - if name == pu.Name { - return pu - } else { - for i := 0; i < len(pu.Children); i++ { - found := GetPredictiveUnit(&pu.Children[i], name) - if found != nil { - return found - } - } - return nil - } -} - -// if engine is not separated then this tells us which pu it should go on, as the mutating webhook handler has set host as localhost on the pu -func GetEnginePredictiveUnit(pu *PredictiveUnit) *PredictiveUnit { - if pu.Endpoint != nil && pu.Endpoint.ServiceHost == "localhost" { - return pu - } else { - for i := 0; i < len(pu.Children); i++ { - found := GetEnginePredictiveUnit(&pu.Children[i]) - if found != nil { - return found - } - } - return nil - } -} - -func GetPredictiveUnitList(p *PredictiveUnit) (list []*PredictiveUnit) { - list = append(list, p) - - for i := 0; i < len(p.Children); i++ { - pu := &p.Children[i] - list = append(list, GetPredictiveUnitList(pu)...) - } - return list -} - -func cleanContainerName(name string) string { - var re = regexp.MustCompile("[^-a-z0-9]") - return re.ReplaceAllString(strings.ToLower(name), "-") -} - -func GetContainerServiceName(mlDep *SeldonDeployment, predictorSpec PredictorSpec, c *v1.Container) string { - containerImageName := cleanContainerName(c.Image) - svcName := mlDep.Spec.Name + "-" + predictorSpec.Name + "-" + c.Name - if containerImageName != "" { - svcName = svcName + "-" + containerImageName - } - if len(svcName) > 63 { - svcName = "seldon" + "-" + containerImageName + "-" + hash(svcName) - if len(svcName) > 63 { - return "seldon-" + hash(svcName) - } else { - return svcName - } - } else { - return svcName - } -} - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// SeldonDeploymentSpec defines the desired state of SeldonDeployment -type SeldonDeploymentSpec struct { - Name string `json:"name,omitempty" protobuf:"string,1,opt,name=name"` - Predictors []PredictorSpec `json:"predictors" protobuf:"bytes,2,opt,name=name"` - OauthKey string `json:"oauth_key,omitempty" protobuf:"string,3,opt,name=oauth_key"` - OauthSecret string `json:"oauth_secret,omitempty" protobuf:"string,4,opt,name=oauth_secret"` - Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,5,opt,name=annotations"` -} - -type PredictorSpec struct { - Name string `json:"name" protobuf:"string,1,opt,name=name"` - Graph *PredictiveUnit `json:"graph" protobuf:"bytes,2,opt,name=predictiveUnit"` - ComponentSpecs []*SeldonPodSpec `json:"componentSpecs,omitempty" protobuf:"bytes,3,opt,name=componentSpecs"` - Replicas int32 `json:"replicas,omitempty" protobuf:"string,4,opt,name=replicas"` - Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,5,opt,name=annotations"` - EngineResources v1.ResourceRequirements `json:"engineResources,omitempty" protobuf:"bytes,6,opt,name=engineResources"` - Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,7,opt,name=labels"` - SvcOrchSpec SvcOrchSpec `json:"svcOrchSpec,omitempty" protobuf:"bytes,8,opt,name=svcOrchSpec"` - Traffic int32 `json:"traffic,omitempty" protobuf:"bytes,9,opt,name=traffic"` - Explainer Explainer `json:"explainer,omitempty" protobuf:"bytes,10,opt,name=explainer"` - Shadow bool `json:"shadow,omitempty" protobuf:"bytes,11,opt,name=shadow"` -} - -type SvcOrchSpec struct { - Resources *v1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` - Env []*v1.EnvVar `json:"env,omitempty" protobuf:"bytes,2,opt,name=env"` -} - -type AlibiExplainerType string - -const ( - AlibiAnchorsTabularExplainer AlibiExplainerType = "AnchorTabular" - AlibiAnchorsImageExplainer AlibiExplainerType = "AnchorImages" - AlibiAnchorsTextExplainer AlibiExplainerType = "AnchorText" - AlibiCounterfactualsExplainer AlibiExplainerType = "Counterfactuals" - AlibiContrastiveExplainer AlibiExplainerType = "Contrastive" -) - -type Explainer struct { - Type AlibiExplainerType `json:"type,omitempty" protobuf:"string,1,opt,name=type"` - ModelUri string `json:"modelUri,omitempty" protobuf:"string,2,opt,name=modelUri"` - ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"string,3,opt,name=serviceAccountName"` - ContainerSpec v1.Container `json:"containerSpec,omitempty" protobuf:"bytes,4,opt,name=containerSpec"` - Config map[string]string `json:"config,omitempty" protobuf:"bytes,5,opt,name=config"` - Endpoint *Endpoint `json:"endpoint,omitempty" protobuf:"bytes,6,opt,name=endpoint"` - EnvSecretRefName string `json:"envSecretRefName,omitempty" protobuf:"bytes,7,opt,name=envSecretRefName"` -} - -type SeldonPodSpec struct { - Metadata metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - Spec v1.PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` - HpaSpec *SeldonHpaSpec `json:"hpaSpec,omitempty" protobuf:"bytes,3,opt,name=hpaSpec"` -} - -type SeldonHpaSpec struct { - MinReplicas *int32 `json:"minReplicas,omitempty" protobuf:"int,1,opt,name=minReplicas"` - MaxReplicas int32 `json:"maxReplicas" protobuf:"int,2,opt,name=maxReplicas"` - Metrics []autoscalingv2beta2.MetricSpec `json:"metrics,omitempty" protobuf:"bytes,3,opt,name=metrics"` -} - -type PredictiveUnitType string - -const ( - UNKNOWN_TYPE PredictiveUnitType = "UNKNOWN_TYPE" - ROUTER PredictiveUnitType = "ROUTER" - COMBINER PredictiveUnitType = "COMBINER" - MODEL PredictiveUnitType = "MODEL" - TRANSFORMER PredictiveUnitType = "TRANSFORMER" - OUTPUT_TRANSFORMER PredictiveUnitType = "OUTPUT_TRANSFORMER" -) - -type PredictiveUnitImplementation string - -const ( - UNKNOWN_IMPLEMENTATION PredictiveUnitImplementation = "UNKNOWN_IMPLEMENTATION" - SIMPLE_MODEL PredictiveUnitImplementation = "SIMPLE_MODEL" - SIMPLE_ROUTER PredictiveUnitImplementation = "SIMPLE_ROUTER" - RANDOM_ABTEST PredictiveUnitImplementation = "RANDOM_ABTEST" - AVERAGE_COMBINER PredictiveUnitImplementation = "AVERAGE_COMBINER" -) - -type PredictiveUnitMethod string - -const ( - TRANSFORM_INPUT PredictiveUnitMethod = "TRANSFORM_INPUT" - TRANSFORM_OUTPUT PredictiveUnitMethod = "TRANSFORM_OUTPUT" - ROUTE PredictiveUnitMethod = "ROUTE" - AGGREGATE PredictiveUnitMethod = "AGGREGATE" - SEND_FEEDBACK PredictiveUnitMethod = "SEND_FEEDBACK" -) - -type EndpointType string - -const ( - REST EndpointType = "REST" - GRPC EndpointType = "GRPC" -) - -type Endpoint struct { - ServiceHost string `json:"service_host,omitempty" protobuf:"string,1,opt,name=service_host"` - ServicePort int32 `json:"service_port,omitempty" protobuf:"int32,2,opt,name=service_port"` - Type EndpointType `json:"type,omitempty" protobuf:"int,3,opt,name=type"` -} - -type ParmeterType string - -const ( - INT ParmeterType = "INT" - FLOAT ParmeterType = "FLOAT" - DOUBLE ParmeterType = "DOUBLE" - STRING ParmeterType = "STRING" - BOOL ParmeterType = "BOOL" -) - -type Parameter struct { - Name string `json:"name" protobuf:"string,1,opt,name=name"` - Value string `json:"value" protobuf:"string,2,opt,name=value"` - Type ParmeterType `json:"type" protobuf:"int,3,opt,name=type"` -} - -type PredictiveUnit struct { - Name string `json:"name" protobuf:"string,1,opt,name=name"` - Children []PredictiveUnit `json:"children,omitempty" protobuf:"bytes,2,opt,name=children"` - Type *PredictiveUnitType `json:"type,omitempty" protobuf:"int,3,opt,name=type"` - Implementation *PredictiveUnitImplementation `json:"implementation,omitempty" protobuf:"int,4,opt,name=implementation"` - Methods *[]PredictiveUnitMethod `json:"methods,omitempty" protobuf:"int,5,opt,name=methods"` - Endpoint *Endpoint `json:"endpoint,omitempty" protobuf:"bytes,6,opt,name=endpoint"` - Parameters []Parameter `json:"parameters,omitempty" protobuf:"bytes,7,opt,name=parameters"` - ModelURI string `json:"modelUri,omitempty" protobuf:"bytes,8,opt,name=modelUri"` - ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,9,opt,name=serviceAccountName"` - EnvSecretRefName string `json:"envSecretRefName,omitempty" protobuf:"bytes,10,opt,name=envSecretRefName"` -} - -type DeploymentStatus struct { - Name string `json:"name,omitempty" protobuf:"string,1,opt,name=name"` - Status string `json:"status,omitempty" protobuf:"string,2,opt,name=status"` - Description string `json:"description,omitempty" protobuf:"string,3,opt,name=description"` - Replicas int32 `json:"replicas,omitempty" protobuf:"string,4,opt,name=replicas"` - AvailableReplicas int32 `json:"availableReplicas,omitempty" protobuf:"string,5,opt,name=availableRelicas"` - ExplainerFor string `json:"explainerFor,omitempty" protobuf:"string,6,opt,name=explainerFor"` -} - -type ServiceStatus struct { - SvcName string `json:"svcName,omitempty" protobuf:"string,1,opt,name=svcName"` - HttpEndpoint string `json:"httpEndpoint,omitempty" protobuf:"string,2,opt,name=httpEndpoint"` - GrpcEndpoint string `json:"grpcEndpoint,omitempty" protobuf:"string,3,opt,name=grpcEndpoint"` - ExplainerFor string `json:"explainerFor,omitempty" protobuf:"string,4,opt,name=explainerFor"` -} - -// SeldonDeploymentStatus defines the observed state of SeldonDeployment -type SeldonDeploymentStatus struct { - State string `json:"state,omitempty" protobuf:"string,1,opt,name=state"` - Description string `json:"description,omitempty" protobuf:"string,2,opt,name=description"` - DeploymentStatus map[string]DeploymentStatus `json:"deploymentStatus,omitempty" protobuf:"bytes,3,opt,name=deploymentStatus"` - ServiceStatus map[string]ServiceStatus `json:"serviceStatus,omitempty" protobuf:"bytes,4,opt,name=serviceStatus"` -} - // +genclient // +genclient:noStatus // +kubebuilder:object:root=true @@ -373,8 +34,8 @@ type SeldonDeployment struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec SeldonDeploymentSpec `json:"spec,omitempty"` - Status SeldonDeploymentStatus `json:"status,omitempty"` + Spec seldonv1.SeldonDeploymentSpec `json:"spec,omitempty"` + Status seldonv1.SeldonDeploymentStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true diff --git a/operator/apis/machinelearning/v1alpha2/seldondeployment_webhook.go b/operator/apis/machinelearning/v1alpha2/seldondeployment_webhook.go index 2a9ba27670..85accc84b4 100644 --- a/operator/apis/machinelearning/v1alpha2/seldondeployment_webhook.go +++ b/operator/apis/machinelearning/v1alpha2/seldondeployment_webhook.go @@ -17,79 +17,19 @@ limitations under the License. package v1alpha2 import ( - "context" - "encoding/json" - "fmt" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - k8types "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/validation/field" - "os" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" "sigs.k8s.io/controller-runtime/pkg/webhook" - "strconv" ) var ( // log is for logging in this package. - seldondeploymentlog = logf.Log.WithName("seldondeployment") - ControllerNamespace = GetEnv("POD_NAMESPACE", "seldon-system") - ControllerConfigMapName = "seldon-config" - C client.Client + seldondeploymentlog = logf.Log.WithName("seldondeployment") + C client.Client ) -const PredictorServerConfigMapKeyName = "predictor_servers" - -type PredictorImageConfig struct { - ContainerImage string `json:"image"` - DefaultImageVersion string `json:"defaultImageVersion"` -} - -type PredictorServerConfig struct { - Tensorflow bool `json:"tensorflow,omitempty"` - TensorflowImage string `json:"tfImage,omitempty"` - RestConfig PredictorImageConfig `json:"rest,omitempty"` - GrpcConfig PredictorImageConfig `json:"grpc,omitempty"` -} - -// Get an environment variable given by key or return the fallback. -func GetEnv(key, fallback string) string { - if value, ok := os.LookupEnv(key); ok { - return value - } - return fallback -} - -func getPredictorServerConfigs() (map[string]PredictorServerConfig, error) { - configMap := &corev1.ConfigMap{} - - err := C.Get(context.TODO(), k8types.NamespacedName{Name: ControllerConfigMapName, Namespace: ControllerNamespace}, configMap) - - if err != nil { - fmt.Println("Failed to find config map " + ControllerConfigMapName) - fmt.Println(err) - return nil, err - } - return getPredictorServerConfigsFromMap(configMap) -} - -func getPredictorServerConfigsFromMap(configMap *corev1.ConfigMap) (map[string]PredictorServerConfig, error) { - predictorServerConfig := make(map[string]PredictorServerConfig) - if predictorConfig, ok := configMap.Data[PredictorServerConfigMapKeyName]; ok { - err := json.Unmarshal([]byte(predictorConfig), &predictorServerConfig) - if err != nil { - panic(fmt.Errorf("Unable to unmarshall %v json string due to %v ", PredictorServerConfigMapKeyName, err)) - } - } - - return predictorServerConfig, nil -} - func (r *SeldonDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error { C = mgr.GetClient() return ctrl.NewWebhookManagedBy(mgr). @@ -99,396 +39,16 @@ func (r *SeldonDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error { var _ webhook.Defaulter = &SeldonDeployment{} -func GetContainerForPredictiveUnit(p *PredictorSpec, name string) *corev1.Container { - for j := 0; j < len(p.ComponentSpecs); j++ { - cSpec := p.ComponentSpecs[j] - for k := 0; k < len(cSpec.Spec.Containers); k++ { - c := &cSpec.Spec.Containers[k] - if c.Name == name { - return c - } - } - } - return nil -} - -func GetPort(name string, ports []corev1.ContainerPort) *corev1.ContainerPort { - for i := 0; i < len(ports); i++ { - if ports[i].Name == name { - return &ports[i] - } - } - return nil -} - -func IsPrepack(pu *PredictiveUnit) bool { - isPrepack := len(*pu.Implementation) > 0 && *pu.Implementation != SIMPLE_MODEL && *pu.Implementation != SIMPLE_ROUTER && *pu.Implementation != RANDOM_ABTEST && *pu.Implementation != AVERAGE_COMBINER && *pu.Implementation != UNKNOWN_IMPLEMENTATION - return isPrepack -} - -func GetPrepackServerConfig(serverName string) PredictorServerConfig { - ServersConfigs, err := getPredictorServerConfigs() - - if err != nil { - seldondeploymentlog.Error(err, "Failed to read prepacked model servers from configmap") - } - ServerConfig, ok := ServersConfigs[serverName] - if !ok { - seldondeploymentlog.Error(nil, "No entry in predictors map for "+serverName) - } - return ServerConfig -} - -func SetImageNameForPrepackContainer(pu *PredictiveUnit, c *corev1.Container) { - //Add missing fields - // Add image - if c.Image == "" { - - ServerConfig := GetPrepackServerConfig(string(*pu.Implementation)) - - if pu.Endpoint.Type == REST { - c.Image = ServerConfig.RestConfig.ContainerImage + ":" + ServerConfig.RestConfig.DefaultImageVersion - } else { - c.Image = ServerConfig.GrpcConfig.ContainerImage + ":" + ServerConfig.GrpcConfig.DefaultImageVersion - } - - } -} - -// ----- - -func addDefaultsToGraph(pu *PredictiveUnit) { - if pu.Type == nil { - ty := UNKNOWN_TYPE - pu.Type = &ty - } - if pu.Implementation == nil { - im := UNKNOWN_IMPLEMENTATION - pu.Implementation = &im - } - for i := 0; i < len(pu.Children); i++ { - addDefaultsToGraph(&pu.Children[i]) - } -} - -func getUpdatePortNumMap(name string, nextPortNum *int32, portMap map[string]int32) int32 { - if _, present := portMap[name]; !present { - portMap[name] = *nextPortNum - *nextPortNum++ - } - return portMap[name] -} - -func (r *SeldonDeployment) DefaultSeldonDeployment() { - - var firstPuPortNum int32 = 9000 - if env_preditive_unit_service_port, ok := os.LookupEnv("PREDICTIVE_UNIT_SERVICE_PORT"); ok { - portNum, err := strconv.Atoi(env_preditive_unit_service_port) - if err != nil { - seldondeploymentlog.Error(err, "Failed to decode PREDICTIVE_UNIT_SERVICE_PORT will use default 9000", "value", env_preditive_unit_service_port) - } else { - firstPuPortNum = int32(portNum) - } - } - nextPortNum := firstPuPortNum - - portMap := map[string]int32{} - - if r.ObjectMeta.Namespace == "" { - r.ObjectMeta.Namespace = "default" - } - - for i := 0; i < len(r.Spec.Predictors); i++ { - p := r.Spec.Predictors[i] - if p.Graph.Type == nil { - ty := UNKNOWN_TYPE - p.Graph.Type = &ty - } - // Add version label for predictor if not present - if p.Labels == nil { - p.Labels = map[string]string{} - } - if _, present := p.Labels["version"]; !present { - p.Labels["version"] = p.Name - } - addDefaultsToGraph(p.Graph) - - r.Spec.Predictors[i] = p - - for j := 0; j < len(p.ComponentSpecs); j++ { - cSpec := r.Spec.Predictors[i].ComponentSpecs[j] - - // add service details for each container - looping this way as if containers in same pod and its the engine pod both need to be localhost - for k := 0; k < len(cSpec.Spec.Containers); k++ { - con := &cSpec.Spec.Containers[k] - - getUpdatePortNumMap(con.Name, &nextPortNum, portMap) - - portNum := portMap[con.Name] - - pu := GetPredictiveUnit(p.Graph, con.Name) - - if pu != nil { - - if pu.Endpoint == nil { - pu.Endpoint = &Endpoint{Type: REST} - } - var portType string - if pu.Endpoint.Type == GRPC { - portType = "grpc" - } else { - portType = "http" - } - - if con != nil { - existingPort := GetPort(portType, con.Ports) - if existingPort != nil { - portNum = existingPort.ContainerPort - } - - volFound := false - for _, vol := range con.VolumeMounts { - if vol.Name == PODINFO_VOLUME_NAME { - volFound = true - } - } - if !volFound { - con.VolumeMounts = append(con.VolumeMounts, corev1.VolumeMount{ - Name: PODINFO_VOLUME_NAME, - MountPath: PODINFO_VOLUME_PATH, - }) - } - } - - // Set ports and hostname in predictive unit so engine can read it from SDep - // if this is the first componentSpec then it's the one to put the engine in - note using outer loop counter here - if _, hasSeparateEnginePod := r.Spec.Annotations[ANNOTATION_SEPARATE_ENGINE]; j == 0 && !hasSeparateEnginePod { - pu.Endpoint.ServiceHost = "localhost" - } else { - containerServiceValue := GetContainerServiceName(r, p, con) - pu.Endpoint.ServiceHost = containerServiceValue + "." + r.ObjectMeta.Namespace + ".svc.cluster.local." - } - pu.Endpoint.ServicePort = portNum - } - } - } - - pus := GetPredictiveUnitList(p.Graph) - - //some pus might not have a container spec so pick those up - for l := 0; l < len(pus); l++ { - pu := pus[l] - - if IsPrepack(pu) { - - con := GetContainerForPredictiveUnit(&p, pu.Name) - - existing := con != nil - if !existing { - con = &corev1.Container{ - Name: pu.Name, - VolumeMounts: []corev1.VolumeMount{ - { - Name: PODINFO_VOLUME_NAME, - MountPath: PODINFO_VOLUME_PATH, - }, - }, - } - } - - // Add a default REST endpoint if none provided - // pu needs to have an endpoint as engine reads it from SDep in order to direct graph traffic - // probes etc will be added later by controller - if pu.Endpoint == nil { - pu.Endpoint = &Endpoint{Type: REST} - } - var portType string - if pu.Endpoint.Type == GRPC { - portType = "grpc" - } else { - portType = "http" - } - - SetImageNameForPrepackContainer(pu, con) - - // if new Add container to componentSpecs - if !existing { - if len(p.ComponentSpecs) > 0 { - p.ComponentSpecs[0].Spec.Containers = append(p.ComponentSpecs[0].Spec.Containers, *con) - } else { - podSpec := SeldonPodSpec{ - Metadata: metav1.ObjectMeta{CreationTimestamp: metav1.Now()}, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{*con}, - }, - } - p.ComponentSpecs = []*SeldonPodSpec{&podSpec} - - // p is a copy so update the entry - r.Spec.Predictors[i] = p - } - } - - getUpdatePortNumMap(con.Name, &nextPortNum, portMap) - portNum := portMap[pu.Name] - - if con != nil { - existingPort := GetPort(portType, con.Ports) - if existingPort != nil { - portNum = existingPort.ContainerPort - } - - volFound := false - for _, vol := range con.VolumeMounts { - if vol.Name == PODINFO_VOLUME_NAME { - volFound = true - } - } - if !volFound { - con.VolumeMounts = append(con.VolumeMounts, corev1.VolumeMount{ - Name: PODINFO_VOLUME_NAME, - MountPath: PODINFO_VOLUME_PATH, - }) - } - } - // Set ports and hostname in predictive unit so engine can read it from SDep - // if this is the firstPuPortNum then we've not added engine yet so put the engine in here - if pu.Endpoint.ServiceHost == "" { - if _, hasSeparateEnginePod := r.Spec.Annotations[ANNOTATION_SEPARATE_ENGINE]; !hasSeparateEnginePod { - pu.Endpoint.ServiceHost = "localhost" - } else { - containerServiceValue := GetContainerServiceName(r, p, con) - pu.Endpoint.ServiceHost = containerServiceValue + "." + r.ObjectMeta.Namespace + ".svc.cluster.local." - } - } - if pu.Endpoint.ServicePort == 0 { - pu.Endpoint.ServicePort = portNum - } - - } - - } - - } - -} - -// ----- - -// --- Validating - -// Check the predictive units to ensure the graph matches up with defined containers. -func checkPredictiveUnits(pu *PredictiveUnit, p *PredictorSpec, fldPath *field.Path, allErrs field.ErrorList) field.ErrorList { - if *pu.Implementation == UNKNOWN_IMPLEMENTATION { - - if GetContainerForPredictiveUnit(p, pu.Name) == nil { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "Can't find container for Predictive Unit")) - } - - if *pu.Type == UNKNOWN_TYPE && (pu.Methods == nil || len(*pu.Methods) == 0) { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "Predictive Unit has no implementation methods defined. Change to a known type or add what methods it defines")) - } - - } else if IsPrepack(pu) { - if pu.ModelURI == "" { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "Predictive unit modelUri required when using standalone servers")) - } - c := GetContainerForPredictiveUnit(p, pu.Name) - - if c == nil || c.Image == "" { - - ServersConfigs, err := getPredictorServerConfigs() - - if err != nil { - seldondeploymentlog.Error(err, "Failed to read prepacked model servers from configmap") - } - - _, ok := ServersConfigs[string(*pu.Implementation)] - if !ok { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "No entry in predictors map for "+string(*pu.Implementation))) - } - } - } - - for i := 0; i < len(pu.Children); i++ { - allErrs = checkPredictiveUnits(&pu.Children[i], p, fldPath.Index(i), allErrs) - } - - return allErrs -} - -func checkTraffic(mlDep *SeldonDeployment, fldPath *field.Path, allErrs field.ErrorList) field.ErrorList { - var trafficSum int32 = 0 - var shadows int = 0 - for i := 0; i < len(mlDep.Spec.Predictors); i++ { - p := mlDep.Spec.Predictors[i] - trafficSum = trafficSum + p.Traffic - - if p.Shadow == true { - shadows += 1 - } - } - if trafficSum != 100 && (len(mlDep.Spec.Predictors)-shadows) > 1 { - allErrs = append(allErrs, field.Invalid(fldPath, mlDep.Name, "Traffic must sum to 100 for multiple predictors")) - } - if trafficSum > 0 && trafficSum < 100 && len(mlDep.Spec.Predictors) == 1 { - allErrs = append(allErrs, field.Invalid(fldPath, mlDep.Name, "Traffic must sum be 100 for a single predictor when set")) - } - - return allErrs -} - -func sizeOfGraph(p *PredictiveUnit) int { - count := 0 - for _, child := range p.Children { - count = count + sizeOfGraph(&child) - } - return count + 1 -} - -func (r *SeldonDeployment) validateSeldonDeployment() error { - var allErrs field.ErrorList - - predictorNames := make(map[string]bool) - for i, p := range r.Spec.Predictors { - - _, noEngine := p.Annotations[ANNOTATION_NO_ENGINE] - if noEngine && sizeOfGraph(p.Graph) > 1 { - fldPath := field.NewPath("spec").Child("predictors").Index(i) - allErrs = append(allErrs, field.Invalid(fldPath, p.Name, "Running without engine only valid for single element graphs")) - } - - if _, present := predictorNames[p.Name]; present { - fldPath := field.NewPath("spec").Child("predictors").Index(i) - allErrs = append(allErrs, field.Invalid(fldPath, p.Name, "Duplicate predictor name")) - } - predictorNames[p.Name] = true - allErrs = checkPredictiveUnits(p.Graph, &p, field.NewPath("spec").Child("predictors").Index(i).Child("graph"), allErrs) - } - - allErrs = checkTraffic(r, field.NewPath("spec"), allErrs) - - if len(allErrs) == 0 { - return nil - } - - return apierrors.NewInvalid( - schema.GroupKind{Group: "machinelearing.seldon.io", Kind: "SeldonDeployment"}, - r.Name, allErrs) - -} - -/// --- - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - // +kubebuilder:webhook:path=/mutate-machinelearning-seldon-io-v1alpha2-seldondeployment,mutating=true,failurePolicy=fail,groups=machinelearning.seldon.io,resources=seldondeployments,verbs=create;update,versions=v1alpha2,name=mseldondeployment.kb.io // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *SeldonDeployment) Default() { - seldondeploymentlog.Info("Defaulting web hook called", "name", r.Name) + seldondeploymentlog.Info("Defaulting v1alpha2 webhook called", "name", r.Name) - r.DefaultSeldonDeployment() + if r.ObjectMeta.Namespace == "" { + r.ObjectMeta.Namespace = "default" + } + r.Spec.DefaultSeldonDeployment(r.ObjectMeta.Namespace) } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. @@ -498,21 +58,21 @@ var _ webhook.Validator = &SeldonDeployment{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateCreate() error { - seldondeploymentlog.Info("Validating Webhook called for CREATE", "name", r.Name) + seldondeploymentlog.Info("Validating v1alpha2 Webhook called for CREATE", "name", r.Name) - return r.validateSeldonDeployment() + return r.Spec.ValidateSeldonDeployment() } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateUpdate(old runtime.Object) error { - seldondeploymentlog.Info("Validating webhook called for UPDATE", "name", r.Name) + seldondeploymentlog.Info("Validating v1alpha2 webhook called for UPDATE", "name", r.Name) - return r.validateSeldonDeployment() + return r.Spec.ValidateSeldonDeployment() } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateDelete() error { - seldondeploymentlog.Info("Validating webhook called for DELETE", "name", r.Name) + seldondeploymentlog.Info("Validating v1alpha2 webhook called for DELETE", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. return nil diff --git a/operator/apis/machinelearning/v1alpha2/zz_generated.deepcopy.go b/operator/apis/machinelearning/v1alpha2/zz_generated.deepcopy.go index 5394dca876..7773e05adc 100644 --- a/operator/apis/machinelearning/v1alpha2/zz_generated.deepcopy.go +++ b/operator/apis/machinelearning/v1alpha2/zz_generated.deepcopy.go @@ -21,215 +21,9 @@ limitations under the License. package v1alpha2 import ( - "k8s.io/api/autoscaling/v2beta1" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus. -func (in *DeploymentStatus) DeepCopy() *DeploymentStatus { - if in == nil { - return nil - } - out := new(DeploymentStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Endpoint) DeepCopyInto(out *Endpoint) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint. -func (in *Endpoint) DeepCopy() *Endpoint { - if in == nil { - return nil - } - out := new(Endpoint) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Explainer) DeepCopyInto(out *Explainer) { - *out = *in - in.ContainerSpec.DeepCopyInto(&out.ContainerSpec) - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Endpoint != nil { - in, out := &in.Endpoint, &out.Endpoint - *out = new(Endpoint) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Explainer. -func (in *Explainer) DeepCopy() *Explainer { - if in == nil { - return nil - } - out := new(Explainer) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Parameter) DeepCopyInto(out *Parameter) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameter. -func (in *Parameter) DeepCopy() *Parameter { - if in == nil { - return nil - } - out := new(Parameter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictiveUnit) DeepCopyInto(out *PredictiveUnit) { - *out = *in - if in.Children != nil { - in, out := &in.Children, &out.Children - *out = make([]PredictiveUnit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Type != nil { - in, out := &in.Type, &out.Type - *out = new(PredictiveUnitType) - **out = **in - } - if in.Implementation != nil { - in, out := &in.Implementation, &out.Implementation - *out = new(PredictiveUnitImplementation) - **out = **in - } - if in.Methods != nil { - in, out := &in.Methods, &out.Methods - *out = new([]PredictiveUnitMethod) - if **in != nil { - in, out := *in, *out - *out = make([]PredictiveUnitMethod, len(*in)) - copy(*out, *in) - } - } - if in.Endpoint != nil { - in, out := &in.Endpoint, &out.Endpoint - *out = new(Endpoint) - **out = **in - } - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - *out = make([]Parameter, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictiveUnit. -func (in *PredictiveUnit) DeepCopy() *PredictiveUnit { - if in == nil { - return nil - } - out := new(PredictiveUnit) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictorImageConfig) DeepCopyInto(out *PredictorImageConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictorImageConfig. -func (in *PredictorImageConfig) DeepCopy() *PredictorImageConfig { - if in == nil { - return nil - } - out := new(PredictorImageConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictorServerConfig) DeepCopyInto(out *PredictorServerConfig) { - *out = *in - out.RestConfig = in.RestConfig - out.GrpcConfig = in.GrpcConfig -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictorServerConfig. -func (in *PredictorServerConfig) DeepCopy() *PredictorServerConfig { - if in == nil { - return nil - } - out := new(PredictorServerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictorSpec) DeepCopyInto(out *PredictorSpec) { - *out = *in - if in.Graph != nil { - in, out := &in.Graph, &out.Graph - *out = new(PredictiveUnit) - (*in).DeepCopyInto(*out) - } - if in.ComponentSpecs != nil { - in, out := &in.ComponentSpecs, &out.ComponentSpecs - *out = make([]*SeldonPodSpec, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(SeldonPodSpec) - (*in).DeepCopyInto(*out) - } - } - } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.EngineResources.DeepCopyInto(&out.EngineResources) - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.SvcOrchSpec.DeepCopyInto(&out.SvcOrchSpec) - in.Explainer.DeepCopyInto(&out.Explainer) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictorSpec. -func (in *PredictorSpec) DeepCopy() *PredictorSpec { - if in == nil { - return nil - } - out := new(PredictorSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SeldonDeployment) DeepCopyInto(out *SeldonDeployment) { *out = *in @@ -288,156 +82,3 @@ func (in *SeldonDeploymentList) DeepCopyObject() runtime.Object { } return nil } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonDeploymentSpec) DeepCopyInto(out *SeldonDeploymentSpec) { - *out = *in - if in.Predictors != nil { - in, out := &in.Predictors, &out.Predictors - *out = make([]PredictorSpec, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonDeploymentSpec. -func (in *SeldonDeploymentSpec) DeepCopy() *SeldonDeploymentSpec { - if in == nil { - return nil - } - out := new(SeldonDeploymentSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonDeploymentStatus) DeepCopyInto(out *SeldonDeploymentStatus) { - *out = *in - if in.DeploymentStatus != nil { - in, out := &in.DeploymentStatus, &out.DeploymentStatus - *out = make(map[string]DeploymentStatus, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.ServiceStatus != nil { - in, out := &in.ServiceStatus, &out.ServiceStatus - *out = make(map[string]ServiceStatus, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonDeploymentStatus. -func (in *SeldonDeploymentStatus) DeepCopy() *SeldonDeploymentStatus { - if in == nil { - return nil - } - out := new(SeldonDeploymentStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonHpaSpec) DeepCopyInto(out *SeldonHpaSpec) { - *out = *in - if in.MinReplicas != nil { - in, out := &in.MinReplicas, &out.MinReplicas - *out = new(int32) - **out = **in - } - if in.Metrics != nil { - in, out := &in.Metrics, &out.Metrics - *out = make([]v2beta1.MetricSpec, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonHpaSpec. -func (in *SeldonHpaSpec) DeepCopy() *SeldonHpaSpec { - if in == nil { - return nil - } - out := new(SeldonHpaSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonPodSpec) DeepCopyInto(out *SeldonPodSpec) { - *out = *in - in.Metadata.DeepCopyInto(&out.Metadata) - in.Spec.DeepCopyInto(&out.Spec) - if in.HpaSpec != nil { - in, out := &in.HpaSpec, &out.HpaSpec - *out = new(SeldonHpaSpec) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonPodSpec. -func (in *SeldonPodSpec) DeepCopy() *SeldonPodSpec { - if in == nil { - return nil - } - out := new(SeldonPodSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceStatus) DeepCopyInto(out *ServiceStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceStatus. -func (in *ServiceStatus) DeepCopy() *ServiceStatus { - if in == nil { - return nil - } - out := new(ServiceStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SvcOrchSpec) DeepCopyInto(out *SvcOrchSpec) { - *out = *in - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = new(v1.ResourceRequirements) - (*in).DeepCopyInto(*out) - } - if in.Env != nil { - in, out := &in.Env, &out.Env - *out = make([]*v1.EnvVar, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(v1.EnvVar) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SvcOrchSpec. -func (in *SvcOrchSpec) DeepCopy() *SvcOrchSpec { - if in == nil { - return nil - } - out := new(SvcOrchSpec) - in.DeepCopyInto(out) - return out -} diff --git a/operator/apis/machinelearning/v1alpha3/seldondeployment_types.go b/operator/apis/machinelearning/v1alpha3/seldondeployment_types.go index 15c64bf104..f6e5db1861 100644 --- a/operator/apis/machinelearning/v1alpha3/seldondeployment_types.go +++ b/operator/apis/machinelearning/v1alpha3/seldondeployment_types.go @@ -17,349 +17,10 @@ limitations under the License. package v1alpha3 import ( - "crypto/md5" - "encoding/hex" - "regexp" - "strings" - - autoscalingv2beta2 "k8s.io/api/autoscaling/v2beta1" - v1 "k8s.io/api/core/v1" + seldonv1 "github.com/seldonio/seldon-core/operator/apis/machinelearning/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -const ( - Label_seldon_id = "seldon-deployment-id" - Label_seldon_app = "seldon-app" - Label_svc_orch = "seldon-deployment-contains-svcorch" - - PODINFO_VOLUME_NAME = "podinfo" - PODINFO_VOLUME_PATH = "/etc/podinfo" - - ENV_PREDICTIVE_UNIT_SERVICE_PORT = "PREDICTIVE_UNIT_SERVICE_PORT" - ENV_PREDICTIVE_UNIT_PARAMETERS = "PREDICTIVE_UNIT_PARAMETERS" - ENV_PREDICTIVE_UNIT_ID = "PREDICTIVE_UNIT_ID" - ENV_PREDICTOR_ID = "PREDICTOR_ID" - ENV_SELDON_DEPLOYMENT_ID = "SELDON_DEPLOYMENT_ID" - - ANNOTATION_JAVA_OPTS = "seldon.io/engine-java-opts" - ANNOTATION_SEPARATE_ENGINE = "seldon.io/engine-separate-pod" - ANNOTATION_HEADLESS_SVC = "seldon.io/headless-svc" - ANNOTATION_NO_ENGINE = "seldon.io/no-engine" - ANNOTATION_CUSTOM_SVC_NAME = "seldon.io/svc-name" -) - -func hash(text string) string { - hasher := md5.New() - hasher.Write([]byte(text)) - return hex.EncodeToString(hasher.Sum(nil)) -} - -func containerHash(podSpec *SeldonPodSpec) string { - s := []string{} - for i := 0; i < len(podSpec.Spec.Containers); i++ { - c := podSpec.Spec.Containers[i] - s = append(s, c.Name) - s = append(s, c.Image) - } - key := strings.Join(s, ":") + ";" - return hash(key)[:7] -} - -func createPredictorHash(p *PredictorSpec) string { - s := []string{} - for i := 0; i < len(p.ComponentSpecs); i++ { - s = append(s, containerHash(p.ComponentSpecs[i])) - } - key := strings.Join(s, ",") + "," - return hash(key)[:7] -} - -func GetSeldonDeploymentName(mlDep *SeldonDeployment) string { - name := mlDep.Spec.Name + "-" + mlDep.ObjectMeta.Name - if len(name) > 63 { - return "seldon-" + hash(name) - } else { - return name - } -} - -func GetExplainerDeploymentName(sdepName string, predictorSpec *PredictorSpec) string { - name := sdepName + "-" + predictorSpec.Name + "-explainer" - if len(name) > 63 { - return "seldon-" + hash(name) - } else { - return name - } -} - -func GetDeploymentName(mlDep *SeldonDeployment, predictorSpec PredictorSpec, podSpec *SeldonPodSpec) string { - if podSpec != nil && len(podSpec.Metadata.Name) != 0 { - return podSpec.Metadata.Name - } else { - name := mlDep.Spec.Name + "-" + predictorSpec.Name - if podSpec != nil { - name = name + "-" + containerHash(podSpec) - } - if len(name) > 63 { - return "seldon-" + hash(name) - } else { - return name - } - } -} - -func GetServiceOrchestratorName(mlDep *SeldonDeployment, p *PredictorSpec) string { - svcOrchName := mlDep.Spec.Name + "-" + p.Name + "-svc-orch" + "-" + createPredictorHash(p) - if len(svcOrchName) > 63 { - return "seldon-" + hash(svcOrchName) - } else { - return svcOrchName - } -} - -func GetPredictorKey(mlDep *SeldonDeployment, p *PredictorSpec) string { - if annotation, hasAnnotation := p.Annotations[ANNOTATION_CUSTOM_SVC_NAME]; hasAnnotation { - return annotation - } else { - return getPredictorKeyAutoGenerated(mlDep, p) - } -} - -func getPredictorKeyAutoGenerated(mlDep *SeldonDeployment, p *PredictorSpec) string { - pName := mlDep.Name + "-" + mlDep.Spec.Name + "-" + p.Name - if len(pName) > 63 { - return "seldon-" + hash(pName) - } else { - return pName - } -} - -func GetPredictorServiceNameKey(c *v1.Container) string { - return Label_seldon_app + "-" + c.Name -} - -func GetPredictiveUnit(pu *PredictiveUnit, name string) *PredictiveUnit { - if name == pu.Name { - return pu - } else { - for i := 0; i < len(pu.Children); i++ { - found := GetPredictiveUnit(&pu.Children[i], name) - if found != nil { - return found - } - } - return nil - } -} - -// if engine is not separated then this tells us which pu it should go on, as the mutating webhook handler has set host as localhost on the pu -func GetEnginePredictiveUnit(pu *PredictiveUnit) *PredictiveUnit { - if pu.Endpoint != nil && pu.Endpoint.ServiceHost == "localhost" { - return pu - } else { - for i := 0; i < len(pu.Children); i++ { - found := GetEnginePredictiveUnit(&pu.Children[i]) - if found != nil { - return found - } - } - return nil - } -} - -func GetPredictiveUnitList(p *PredictiveUnit) (list []*PredictiveUnit) { - list = append(list, p) - - for i := 0; i < len(p.Children); i++ { - pu := &p.Children[i] - list = append(list, GetPredictiveUnitList(pu)...) - } - return list -} - -func cleanContainerName(name string) string { - var re = regexp.MustCompile("[^-a-z0-9]") - return re.ReplaceAllString(strings.ToLower(name), "-") -} - -func GetContainerServiceName(mlDep *SeldonDeployment, predictorSpec PredictorSpec, c *v1.Container) string { - containerImageName := cleanContainerName(c.Image) - svcName := mlDep.Spec.Name + "-" + predictorSpec.Name + "-" + c.Name - if containerImageName != "" { - svcName = svcName + "-" + containerImageName - } - if len(svcName) > 63 { - svcName = "seldon" + "-" + containerImageName + "-" + hash(svcName) - if len(svcName) > 63 { - return "seldon-" + hash(svcName) - } else { - return svcName - } - } else { - return svcName - } -} - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - -// SeldonDeploymentSpec defines the desired state of SeldonDeployment -type SeldonDeploymentSpec struct { - Name string `json:"name,omitempty" protobuf:"string,1,opt,name=name"` - Predictors []PredictorSpec `json:"predictors" protobuf:"bytes,2,opt,name=name"` - OauthKey string `json:"oauth_key,omitempty" protobuf:"string,3,opt,name=oauth_key"` - OauthSecret string `json:"oauth_secret,omitempty" protobuf:"string,4,opt,name=oauth_secret"` - Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,5,opt,name=annotations"` -} - -type PredictorSpec struct { - Name string `json:"name" protobuf:"string,1,opt,name=name"` - Graph *PredictiveUnit `json:"graph" protobuf:"bytes,2,opt,name=predictiveUnit"` - ComponentSpecs []*SeldonPodSpec `json:"componentSpecs,omitempty" protobuf:"bytes,3,opt,name=componentSpecs"` - Replicas int32 `json:"replicas,omitempty" protobuf:"string,4,opt,name=replicas"` - Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,5,opt,name=annotations"` - EngineResources v1.ResourceRequirements `json:"engineResources,omitempty" protobuf:"bytes,6,opt,name=engineResources"` - Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,7,opt,name=labels"` - SvcOrchSpec SvcOrchSpec `json:"svcOrchSpec,omitempty" protobuf:"bytes,8,opt,name=svcOrchSpec"` - Traffic int32 `json:"traffic,omitempty" protobuf:"bytes,9,opt,name=traffic"` - Explainer Explainer `json:"explainer,omitempty" protobuf:"bytes,10,opt,name=explainer"` - Shadow bool `json:"shadow,omitempty" protobuf:"bytes,11,opt,name=shadow"` -} - -type SvcOrchSpec struct { - Resources *v1.ResourceRequirements `json:"resources,omitempty" protobuf:"bytes,1,opt,name=resources"` - Env []*v1.EnvVar `json:"env,omitempty" protobuf:"bytes,2,opt,name=env"` -} - -type AlibiExplainerType string - -const ( - AlibiAnchorsTabularExplainer AlibiExplainerType = "AnchorTabular" - AlibiAnchorsImageExplainer AlibiExplainerType = "AnchorImages" - AlibiAnchorsTextExplainer AlibiExplainerType = "AnchorText" - AlibiCounterfactualsExplainer AlibiExplainerType = "Counterfactuals" - AlibiContrastiveExplainer AlibiExplainerType = "Contrastive" -) - -type Explainer struct { - Type AlibiExplainerType `json:"type,omitempty" protobuf:"string,1,opt,name=type"` - ModelUri string `json:"modelUri,omitempty" protobuf:"string,2,opt,name=modelUri"` - ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"string,3,opt,name=serviceAccountName"` - ContainerSpec v1.Container `json:"containerSpec,omitempty" protobuf:"bytes,4,opt,name=containerSpec"` - Config map[string]string `json:"config,omitempty" protobuf:"bytes,5,opt,name=config"` - Endpoint *Endpoint `json:"endpoint,omitempty" protobuf:"bytes,6,opt,name=endpoint"` - EnvSecretRefName string `json:"envSecretRefName,omitempty" protobuf:"bytes,7,opt,name=envSecretRefName"` -} - -type SeldonPodSpec struct { - Metadata metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` - Spec v1.PodSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"` - HpaSpec *SeldonHpaSpec `json:"hpaSpec,omitempty" protobuf:"bytes,3,opt,name=hpaSpec"` -} - -type SeldonHpaSpec struct { - MinReplicas *int32 `json:"minReplicas,omitempty" protobuf:"int,1,opt,name=minReplicas"` - MaxReplicas int32 `json:"maxReplicas" protobuf:"int,2,opt,name=maxReplicas"` - Metrics []autoscalingv2beta2.MetricSpec `json:"metrics,omitempty" protobuf:"bytes,3,opt,name=metrics"` -} - -type PredictiveUnitType string - -const ( - UNKNOWN_TYPE PredictiveUnitType = "UNKNOWN_TYPE" - ROUTER PredictiveUnitType = "ROUTER" - COMBINER PredictiveUnitType = "COMBINER" - MODEL PredictiveUnitType = "MODEL" - TRANSFORMER PredictiveUnitType = "TRANSFORMER" - OUTPUT_TRANSFORMER PredictiveUnitType = "OUTPUT_TRANSFORMER" -) - -type PredictiveUnitImplementation string - -const ( - UNKNOWN_IMPLEMENTATION PredictiveUnitImplementation = "UNKNOWN_IMPLEMENTATION" - SIMPLE_MODEL PredictiveUnitImplementation = "SIMPLE_MODEL" - SIMPLE_ROUTER PredictiveUnitImplementation = "SIMPLE_ROUTER" - RANDOM_ABTEST PredictiveUnitImplementation = "RANDOM_ABTEST" - AVERAGE_COMBINER PredictiveUnitImplementation = "AVERAGE_COMBINER" -) - -type PredictiveUnitMethod string - -const ( - TRANSFORM_INPUT PredictiveUnitMethod = "TRANSFORM_INPUT" - TRANSFORM_OUTPUT PredictiveUnitMethod = "TRANSFORM_OUTPUT" - ROUTE PredictiveUnitMethod = "ROUTE" - AGGREGATE PredictiveUnitMethod = "AGGREGATE" - SEND_FEEDBACK PredictiveUnitMethod = "SEND_FEEDBACK" -) - -type EndpointType string - -const ( - REST EndpointType = "REST" - GRPC EndpointType = "GRPC" -) - -type Endpoint struct { - ServiceHost string `json:"service_host,omitempty" protobuf:"string,1,opt,name=service_host"` - ServicePort int32 `json:"service_port,omitempty" protobuf:"int32,2,opt,name=service_port"` - Type EndpointType `json:"type,omitempty" protobuf:"int,3,opt,name=type"` -} - -type ParmeterType string - -const ( - INT ParmeterType = "INT" - FLOAT ParmeterType = "FLOAT" - DOUBLE ParmeterType = "DOUBLE" - STRING ParmeterType = "STRING" - BOOL ParmeterType = "BOOL" -) - -type Parameter struct { - Name string `json:"name" protobuf:"string,1,opt,name=name"` - Value string `json:"value" protobuf:"string,2,opt,name=value"` - Type ParmeterType `json:"type" protobuf:"int,3,opt,name=type"` -} - -type PredictiveUnit struct { - Name string `json:"name" protobuf:"string,1,opt,name=name"` - Children []PredictiveUnit `json:"children,omitempty" protobuf:"bytes,2,opt,name=children"` - Type *PredictiveUnitType `json:"type,omitempty" protobuf:"int,3,opt,name=type"` - Implementation *PredictiveUnitImplementation `json:"implementation,omitempty" protobuf:"int,4,opt,name=implementation"` - Methods *[]PredictiveUnitMethod `json:"methods,omitempty" protobuf:"int,5,opt,name=methods"` - Endpoint *Endpoint `json:"endpoint,omitempty" protobuf:"bytes,6,opt,name=endpoint"` - Parameters []Parameter `json:"parameters,omitempty" protobuf:"bytes,7,opt,name=parameters"` - ModelURI string `json:"modelUri,omitempty" protobuf:"bytes,8,opt,name=modelUri"` - ServiceAccountName string `json:"serviceAccountName,omitempty" protobuf:"bytes,9,opt,name=serviceAccountName"` - EnvSecretRefName string `json:"envSecretRefName,omitempty" protobuf:"bytes,10,opt,name=envSecretRefName"` -} - -type DeploymentStatus struct { - Name string `json:"name,omitempty" protobuf:"string,1,opt,name=name"` - Status string `json:"status,omitempty" protobuf:"string,2,opt,name=status"` - Description string `json:"description,omitempty" protobuf:"string,3,opt,name=description"` - Replicas int32 `json:"replicas,omitempty" protobuf:"string,4,opt,name=replicas"` - AvailableReplicas int32 `json:"availableReplicas,omitempty" protobuf:"string,5,opt,name=availableRelicas"` - ExplainerFor string `json:"explainerFor,omitempty" protobuf:"string,6,opt,name=explainerFor"` -} - -type ServiceStatus struct { - SvcName string `json:"svcName,omitempty" protobuf:"string,1,opt,name=svcName"` - HttpEndpoint string `json:"httpEndpoint,omitempty" protobuf:"string,2,opt,name=httpEndpoint"` - GrpcEndpoint string `json:"grpcEndpoint,omitempty" protobuf:"string,3,opt,name=grpcEndpoint"` - ExplainerFor string `json:"explainerFor,omitempty" protobuf:"string,4,opt,name=explainerFor"` -} - -// SeldonDeploymentStatus defines the observed state of SeldonDeployment -type SeldonDeploymentStatus struct { - State string `json:"state,omitempty" protobuf:"string,1,opt,name=state"` - Description string `json:"description,omitempty" protobuf:"string,2,opt,name=description"` - DeploymentStatus map[string]DeploymentStatus `json:"deploymentStatus,omitempty" protobuf:"bytes,3,opt,name=deploymentStatus"` - ServiceStatus map[string]ServiceStatus `json:"serviceStatus,omitempty" protobuf:"bytes,4,opt,name=serviceStatus"` -} - // +genclient // +genclient:noStatus // +kubebuilder:object:root=true @@ -373,8 +34,8 @@ type SeldonDeployment struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec SeldonDeploymentSpec `json:"spec,omitempty"` - Status SeldonDeploymentStatus `json:"status,omitempty"` + Spec seldonv1.SeldonDeploymentSpec `json:"spec,omitempty"` + Status seldonv1.SeldonDeploymentStatus `json:"status,omitempty"` } // +kubebuilder:object:root=true diff --git a/operator/apis/machinelearning/v1alpha3/seldondeployment_webhook.go b/operator/apis/machinelearning/v1alpha3/seldondeployment_webhook.go index 23def03c8a..ebe6a5f4a5 100644 --- a/operator/apis/machinelearning/v1alpha3/seldondeployment_webhook.go +++ b/operator/apis/machinelearning/v1alpha3/seldondeployment_webhook.go @@ -17,79 +17,19 @@ limitations under the License. package v1alpha3 import ( - "context" - "encoding/json" - "fmt" - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - k8types "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/validation/field" - "os" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" "sigs.k8s.io/controller-runtime/pkg/webhook" - "strconv" ) var ( // log is for logging in this package. - seldondeploymentlog = logf.Log.WithName("seldondeployment") - ControllerNamespace = GetEnv("POD_NAMESPACE", "seldon-system") - ControllerConfigMapName = "seldon-config" - C client.Client + seldondeploymentlog = logf.Log.WithName("seldondeployment") + C client.Client ) -const PredictorServerConfigMapKeyName = "predictor_servers" - -type PredictorImageConfig struct { - ContainerImage string `json:"image"` - DefaultImageVersion string `json:"defaultImageVersion"` -} - -type PredictorServerConfig struct { - Tensorflow bool `json:"tensorflow,omitempty"` - TensorflowImage string `json:"tfImage,omitempty"` - RestConfig PredictorImageConfig `json:"rest,omitempty"` - GrpcConfig PredictorImageConfig `json:"grpc,omitempty"` -} - -// Get an environment variable given by key or return the fallback. -func GetEnv(key, fallback string) string { - if value, ok := os.LookupEnv(key); ok { - return value - } - return fallback -} - -func getPredictorServerConfigs() (map[string]PredictorServerConfig, error) { - configMap := &corev1.ConfigMap{} - - err := C.Get(context.TODO(), k8types.NamespacedName{Name: ControllerConfigMapName, Namespace: ControllerNamespace}, configMap) - - if err != nil { - fmt.Println("Failed to find config map " + ControllerConfigMapName) - fmt.Println(err) - return nil, err - } - return getPredictorServerConfigsFromMap(configMap) -} - -func getPredictorServerConfigsFromMap(configMap *corev1.ConfigMap) (map[string]PredictorServerConfig, error) { - predictorServerConfig := make(map[string]PredictorServerConfig) - if predictorConfig, ok := configMap.Data[PredictorServerConfigMapKeyName]; ok { - err := json.Unmarshal([]byte(predictorConfig), &predictorServerConfig) - if err != nil { - panic(fmt.Errorf("Unable to unmarshall %v json string due to %v ", PredictorServerConfigMapKeyName, err)) - } - } - - return predictorServerConfig, nil -} - func (r *SeldonDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error { C = mgr.GetClient() return ctrl.NewWebhookManagedBy(mgr). @@ -99,396 +39,16 @@ func (r *SeldonDeployment) SetupWebhookWithManager(mgr ctrl.Manager) error { var _ webhook.Defaulter = &SeldonDeployment{} -func GetContainerForPredictiveUnit(p *PredictorSpec, name string) *corev1.Container { - for j := 0; j < len(p.ComponentSpecs); j++ { - cSpec := p.ComponentSpecs[j] - for k := 0; k < len(cSpec.Spec.Containers); k++ { - c := &cSpec.Spec.Containers[k] - if c.Name == name { - return c - } - } - } - return nil -} - -func GetPort(name string, ports []corev1.ContainerPort) *corev1.ContainerPort { - for i := 0; i < len(ports); i++ { - if ports[i].Name == name { - return &ports[i] - } - } - return nil -} - -func IsPrepack(pu *PredictiveUnit) bool { - isPrepack := len(*pu.Implementation) > 0 && *pu.Implementation != SIMPLE_MODEL && *pu.Implementation != SIMPLE_ROUTER && *pu.Implementation != RANDOM_ABTEST && *pu.Implementation != AVERAGE_COMBINER && *pu.Implementation != UNKNOWN_IMPLEMENTATION - return isPrepack -} - -func GetPrepackServerConfig(serverName string) PredictorServerConfig { - ServersConfigs, err := getPredictorServerConfigs() - - if err != nil { - seldondeploymentlog.Error(err, "Failed to read prepacked model servers from configmap") - } - ServerConfig, ok := ServersConfigs[serverName] - if !ok { - seldondeploymentlog.Error(nil, "No entry in predictors map for "+serverName) - } - return ServerConfig -} - -func SetImageNameForPrepackContainer(pu *PredictiveUnit, c *corev1.Container) { - //Add missing fields - // Add image - if c.Image == "" { - - ServerConfig := GetPrepackServerConfig(string(*pu.Implementation)) - - if pu.Endpoint.Type == REST { - c.Image = ServerConfig.RestConfig.ContainerImage + ":" + ServerConfig.RestConfig.DefaultImageVersion - } else { - c.Image = ServerConfig.GrpcConfig.ContainerImage + ":" + ServerConfig.GrpcConfig.DefaultImageVersion - } - - } -} - -// ----- - -func addDefaultsToGraph(pu *PredictiveUnit) { - if pu.Type == nil { - ty := UNKNOWN_TYPE - pu.Type = &ty - } - if pu.Implementation == nil { - im := UNKNOWN_IMPLEMENTATION - pu.Implementation = &im - } - for i := 0; i < len(pu.Children); i++ { - addDefaultsToGraph(&pu.Children[i]) - } -} - -func getUpdatePortNumMap(name string, nextPortNum *int32, portMap map[string]int32) int32 { - if _, present := portMap[name]; !present { - portMap[name] = *nextPortNum - *nextPortNum++ - } - return portMap[name] -} - -func (r *SeldonDeployment) DefaultSeldonDeployment() { - - var firstPuPortNum int32 = 9000 - if env_preditive_unit_service_port, ok := os.LookupEnv("PREDICTIVE_UNIT_SERVICE_PORT"); ok { - portNum, err := strconv.Atoi(env_preditive_unit_service_port) - if err != nil { - seldondeploymentlog.Error(err, "Failed to decode PREDICTIVE_UNIT_SERVICE_PORT will use default 9000", "value", env_preditive_unit_service_port) - } else { - firstPuPortNum = int32(portNum) - } - } - nextPortNum := firstPuPortNum - - portMap := map[string]int32{} - - if r.ObjectMeta.Namespace == "" { - r.ObjectMeta.Namespace = "default" - } - - for i := 0; i < len(r.Spec.Predictors); i++ { - p := r.Spec.Predictors[i] - if p.Graph.Type == nil { - ty := UNKNOWN_TYPE - p.Graph.Type = &ty - } - // Add version label for predictor if not present - if p.Labels == nil { - p.Labels = map[string]string{} - } - if _, present := p.Labels["version"]; !present { - p.Labels["version"] = p.Name - } - addDefaultsToGraph(p.Graph) - - r.Spec.Predictors[i] = p - - for j := 0; j < len(p.ComponentSpecs); j++ { - cSpec := r.Spec.Predictors[i].ComponentSpecs[j] - - // add service details for each container - looping this way as if containers in same pod and its the engine pod both need to be localhost - for k := 0; k < len(cSpec.Spec.Containers); k++ { - con := &cSpec.Spec.Containers[k] - - getUpdatePortNumMap(con.Name, &nextPortNum, portMap) - - portNum := portMap[con.Name] - - pu := GetPredictiveUnit(p.Graph, con.Name) - - if pu != nil { - - if pu.Endpoint == nil { - pu.Endpoint = &Endpoint{Type: REST} - } - var portType string - if pu.Endpoint.Type == GRPC { - portType = "grpc" - } else { - portType = "http" - } - - if con != nil { - existingPort := GetPort(portType, con.Ports) - if existingPort != nil { - portNum = existingPort.ContainerPort - } - - volFound := false - for _, vol := range con.VolumeMounts { - if vol.Name == PODINFO_VOLUME_NAME { - volFound = true - } - } - if !volFound { - con.VolumeMounts = append(con.VolumeMounts, corev1.VolumeMount{ - Name: PODINFO_VOLUME_NAME, - MountPath: PODINFO_VOLUME_PATH, - }) - } - } - - // Set ports and hostname in predictive unit so engine can read it from SDep - // if this is the first componentSpec then it's the one to put the engine in - note using outer loop counter here - if _, hasSeparateEnginePod := r.Spec.Annotations[ANNOTATION_SEPARATE_ENGINE]; j == 0 && !hasSeparateEnginePod { - pu.Endpoint.ServiceHost = "localhost" - } else { - containerServiceValue := GetContainerServiceName(r, p, con) - pu.Endpoint.ServiceHost = containerServiceValue + "." + r.ObjectMeta.Namespace + ".svc.cluster.local." - } - pu.Endpoint.ServicePort = portNum - } - } - } - - pus := GetPredictiveUnitList(p.Graph) - - //some pus might not have a container spec so pick those up - for l := 0; l < len(pus); l++ { - pu := pus[l] - - if IsPrepack(pu) { - - con := GetContainerForPredictiveUnit(&p, pu.Name) - - existing := con != nil - if !existing { - con = &corev1.Container{ - Name: pu.Name, - VolumeMounts: []corev1.VolumeMount{ - { - Name: PODINFO_VOLUME_NAME, - MountPath: PODINFO_VOLUME_PATH, - }, - }, - } - } - - // Add a default REST endpoint if none provided - // pu needs to have an endpoint as engine reads it from SDep in order to direct graph traffic - // probes etc will be added later by controller - if pu.Endpoint == nil { - pu.Endpoint = &Endpoint{Type: REST} - } - var portType string - if pu.Endpoint.Type == GRPC { - portType = "grpc" - } else { - portType = "http" - } - - SetImageNameForPrepackContainer(pu, con) - - // if new Add container to componentSpecs - if !existing { - if len(p.ComponentSpecs) > 0 { - p.ComponentSpecs[0].Spec.Containers = append(p.ComponentSpecs[0].Spec.Containers, *con) - } else { - podSpec := SeldonPodSpec{ - Metadata: metav1.ObjectMeta{CreationTimestamp: metav1.Now()}, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{*con}, - }, - } - p.ComponentSpecs = []*SeldonPodSpec{&podSpec} - - // p is a copy so update the entry - r.Spec.Predictors[i] = p - } - } - - getUpdatePortNumMap(con.Name, &nextPortNum, portMap) - portNum := portMap[pu.Name] - - if con != nil { - existingPort := GetPort(portType, con.Ports) - if existingPort != nil { - portNum = existingPort.ContainerPort - } - - volFound := false - for _, vol := range con.VolumeMounts { - if vol.Name == PODINFO_VOLUME_NAME { - volFound = true - } - } - if !volFound { - con.VolumeMounts = append(con.VolumeMounts, corev1.VolumeMount{ - Name: PODINFO_VOLUME_NAME, - MountPath: PODINFO_VOLUME_PATH, - }) - } - } - // Set ports and hostname in predictive unit so engine can read it from SDep - // if this is the firstPuPortNum then we've not added engine yet so put the engine in here - if pu.Endpoint.ServiceHost == "" { - if _, hasSeparateEnginePod := r.Spec.Annotations[ANNOTATION_SEPARATE_ENGINE]; !hasSeparateEnginePod { - pu.Endpoint.ServiceHost = "localhost" - } else { - containerServiceValue := GetContainerServiceName(r, p, con) - pu.Endpoint.ServiceHost = containerServiceValue + "." + r.ObjectMeta.Namespace + ".svc.cluster.local." - } - } - if pu.Endpoint.ServicePort == 0 { - pu.Endpoint.ServicePort = portNum - } - - } - - } - - } - -} - -// ----- - -// --- Validating - -// Check the predictive units to ensure the graph matches up with defined containers. -func checkPredictiveUnits(pu *PredictiveUnit, p *PredictorSpec, fldPath *field.Path, allErrs field.ErrorList) field.ErrorList { - if *pu.Implementation == UNKNOWN_IMPLEMENTATION { - - if GetContainerForPredictiveUnit(p, pu.Name) == nil { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "Can't find container for Predictive Unit")) - } - - if *pu.Type == UNKNOWN_TYPE && (pu.Methods == nil || len(*pu.Methods) == 0) { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "Predictive Unit has no implementation methods defined. Change to a known type or add what methods it defines")) - } - - } else if IsPrepack(pu) { - if pu.ModelURI == "" { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "Predictive unit modelUri required when using standalone servers")) - } - c := GetContainerForPredictiveUnit(p, pu.Name) - - if c == nil || c.Image == "" { - - ServersConfigs, err := getPredictorServerConfigs() - - if err != nil { - seldondeploymentlog.Error(err, "Failed to read prepacked model servers from configmap") - } - - _, ok := ServersConfigs[string(*pu.Implementation)] - if !ok { - allErrs = append(allErrs, field.Invalid(fldPath, pu.Name, "No entry in predictors map for "+string(*pu.Implementation))) - } - } - } - - for i := 0; i < len(pu.Children); i++ { - allErrs = checkPredictiveUnits(&pu.Children[i], p, fldPath.Index(i), allErrs) - } - - return allErrs -} - -func checkTraffic(mlDep *SeldonDeployment, fldPath *field.Path, allErrs field.ErrorList) field.ErrorList { - var trafficSum int32 = 0 - var shadows int = 0 - for i := 0; i < len(mlDep.Spec.Predictors); i++ { - p := mlDep.Spec.Predictors[i] - trafficSum = trafficSum + p.Traffic - - if p.Shadow == true { - shadows += 1 - } - } - if trafficSum != 100 && (len(mlDep.Spec.Predictors)-shadows) > 1 { - allErrs = append(allErrs, field.Invalid(fldPath, mlDep.Name, "Traffic must sum to 100 for multiple predictors")) - } - if trafficSum > 0 && trafficSum < 100 && len(mlDep.Spec.Predictors) == 1 { - allErrs = append(allErrs, field.Invalid(fldPath, mlDep.Name, "Traffic must sum be 100 for a single predictor when set")) - } - - return allErrs -} - -func sizeOfGraph(p *PredictiveUnit) int { - count := 0 - for _, child := range p.Children { - count = count + sizeOfGraph(&child) - } - return count + 1 -} - -func (r *SeldonDeployment) validateSeldonDeployment() error { - var allErrs field.ErrorList - - predictorNames := make(map[string]bool) - for i, p := range r.Spec.Predictors { - - _, noEngine := p.Annotations[ANNOTATION_NO_ENGINE] - if noEngine && sizeOfGraph(p.Graph) > 1 { - fldPath := field.NewPath("spec").Child("predictors").Index(i) - allErrs = append(allErrs, field.Invalid(fldPath, p.Name, "Running without engine only valid for single element graphs")) - } - - if _, present := predictorNames[p.Name]; present { - fldPath := field.NewPath("spec").Child("predictors").Index(i) - allErrs = append(allErrs, field.Invalid(fldPath, p.Name, "Duplicate predictor name")) - } - predictorNames[p.Name] = true - allErrs = checkPredictiveUnits(p.Graph, &p, field.NewPath("spec").Child("predictors").Index(i).Child("graph"), allErrs) - } - - allErrs = checkTraffic(r, field.NewPath("spec"), allErrs) - - if len(allErrs) == 0 { - return nil - } - - return apierrors.NewInvalid( - schema.GroupKind{Group: "machinelearing.seldon.io", Kind: "SeldonDeployment"}, - r.Name, allErrs) - -} - -/// --- - -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! - // +kubebuilder:webhook:path=/mutate-machinelearning-seldon-io-v1alpha3-seldondeployment,mutating=true,failurePolicy=fail,groups=machinelearning.seldon.io,resources=seldondeployments,verbs=create;update,versions=v1alpha3,name=mseldondeployment.kb.io // Default implements webhook.Defaulter so a webhook will be registered for the type func (r *SeldonDeployment) Default() { - seldondeploymentlog.Info("Defaulting web hook called", "name", r.Name) + seldondeploymentlog.Info("Defaulting v1alpha3 webhook called", "name", r.Name) - r.DefaultSeldonDeployment() + if r.ObjectMeta.Namespace == "" { + r.ObjectMeta.Namespace = "default" + } + r.Spec.DefaultSeldonDeployment(r.ObjectMeta.Namespace) } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. @@ -498,21 +58,21 @@ var _ webhook.Validator = &SeldonDeployment{} // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateCreate() error { - seldondeploymentlog.Info("Validating Webhook called for CREATE", "name", r.Name) + seldondeploymentlog.Info("Validating v1alpha3 Webhook called for CREATE", "name", r.Name) - return r.validateSeldonDeployment() + return r.Spec.ValidateSeldonDeployment() } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateUpdate(old runtime.Object) error { - seldondeploymentlog.Info("Validating webhook called for UPDATE", "name", r.Name) + seldondeploymentlog.Info("Validating v1alpha3 webhook called for UPDATE", "name", r.Name) - return r.validateSeldonDeployment() + return r.Spec.ValidateSeldonDeployment() } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *SeldonDeployment) ValidateDelete() error { - seldondeploymentlog.Info("Validating webhook called for DELETE", "name", r.Name) + seldondeploymentlog.Info("Validating v1alpha3 webhook called for DELETE", "name", r.Name) // TODO(user): fill in your validation logic upon object deletion. return nil diff --git a/operator/apis/machinelearning/v1alpha3/zz_generated.deepcopy.go b/operator/apis/machinelearning/v1alpha3/zz_generated.deepcopy.go index 0e23a55b2d..0bd925cf28 100644 --- a/operator/apis/machinelearning/v1alpha3/zz_generated.deepcopy.go +++ b/operator/apis/machinelearning/v1alpha3/zz_generated.deepcopy.go @@ -21,215 +21,9 @@ limitations under the License. package v1alpha3 import ( - "k8s.io/api/autoscaling/v2beta1" - "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DeploymentStatus) DeepCopyInto(out *DeploymentStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DeploymentStatus. -func (in *DeploymentStatus) DeepCopy() *DeploymentStatus { - if in == nil { - return nil - } - out := new(DeploymentStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Endpoint) DeepCopyInto(out *Endpoint) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Endpoint. -func (in *Endpoint) DeepCopy() *Endpoint { - if in == nil { - return nil - } - out := new(Endpoint) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Explainer) DeepCopyInto(out *Explainer) { - *out = *in - in.ContainerSpec.DeepCopyInto(&out.ContainerSpec) - if in.Config != nil { - in, out := &in.Config, &out.Config - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Endpoint != nil { - in, out := &in.Endpoint, &out.Endpoint - *out = new(Endpoint) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Explainer. -func (in *Explainer) DeepCopy() *Explainer { - if in == nil { - return nil - } - out := new(Explainer) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Parameter) DeepCopyInto(out *Parameter) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameter. -func (in *Parameter) DeepCopy() *Parameter { - if in == nil { - return nil - } - out := new(Parameter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictiveUnit) DeepCopyInto(out *PredictiveUnit) { - *out = *in - if in.Children != nil { - in, out := &in.Children, &out.Children - *out = make([]PredictiveUnit, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Type != nil { - in, out := &in.Type, &out.Type - *out = new(PredictiveUnitType) - **out = **in - } - if in.Implementation != nil { - in, out := &in.Implementation, &out.Implementation - *out = new(PredictiveUnitImplementation) - **out = **in - } - if in.Methods != nil { - in, out := &in.Methods, &out.Methods - *out = new([]PredictiveUnitMethod) - if **in != nil { - in, out := *in, *out - *out = make([]PredictiveUnitMethod, len(*in)) - copy(*out, *in) - } - } - if in.Endpoint != nil { - in, out := &in.Endpoint, &out.Endpoint - *out = new(Endpoint) - **out = **in - } - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - *out = make([]Parameter, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictiveUnit. -func (in *PredictiveUnit) DeepCopy() *PredictiveUnit { - if in == nil { - return nil - } - out := new(PredictiveUnit) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictorImageConfig) DeepCopyInto(out *PredictorImageConfig) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictorImageConfig. -func (in *PredictorImageConfig) DeepCopy() *PredictorImageConfig { - if in == nil { - return nil - } - out := new(PredictorImageConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictorServerConfig) DeepCopyInto(out *PredictorServerConfig) { - *out = *in - out.RestConfig = in.RestConfig - out.GrpcConfig = in.GrpcConfig -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictorServerConfig. -func (in *PredictorServerConfig) DeepCopy() *PredictorServerConfig { - if in == nil { - return nil - } - out := new(PredictorServerConfig) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PredictorSpec) DeepCopyInto(out *PredictorSpec) { - *out = *in - if in.Graph != nil { - in, out := &in.Graph, &out.Graph - *out = new(PredictiveUnit) - (*in).DeepCopyInto(*out) - } - if in.ComponentSpecs != nil { - in, out := &in.ComponentSpecs, &out.ComponentSpecs - *out = make([]*SeldonPodSpec, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(SeldonPodSpec) - (*in).DeepCopyInto(*out) - } - } - } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.EngineResources.DeepCopyInto(&out.EngineResources) - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - in.SvcOrchSpec.DeepCopyInto(&out.SvcOrchSpec) - in.Explainer.DeepCopyInto(&out.Explainer) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PredictorSpec. -func (in *PredictorSpec) DeepCopy() *PredictorSpec { - if in == nil { - return nil - } - out := new(PredictorSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SeldonDeployment) DeepCopyInto(out *SeldonDeployment) { *out = *in @@ -288,156 +82,3 @@ func (in *SeldonDeploymentList) DeepCopyObject() runtime.Object { } return nil } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonDeploymentSpec) DeepCopyInto(out *SeldonDeploymentSpec) { - *out = *in - if in.Predictors != nil { - in, out := &in.Predictors, &out.Predictors - *out = make([]PredictorSpec, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonDeploymentSpec. -func (in *SeldonDeploymentSpec) DeepCopy() *SeldonDeploymentSpec { - if in == nil { - return nil - } - out := new(SeldonDeploymentSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonDeploymentStatus) DeepCopyInto(out *SeldonDeploymentStatus) { - *out = *in - if in.DeploymentStatus != nil { - in, out := &in.DeploymentStatus, &out.DeploymentStatus - *out = make(map[string]DeploymentStatus, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.ServiceStatus != nil { - in, out := &in.ServiceStatus, &out.ServiceStatus - *out = make(map[string]ServiceStatus, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonDeploymentStatus. -func (in *SeldonDeploymentStatus) DeepCopy() *SeldonDeploymentStatus { - if in == nil { - return nil - } - out := new(SeldonDeploymentStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonHpaSpec) DeepCopyInto(out *SeldonHpaSpec) { - *out = *in - if in.MinReplicas != nil { - in, out := &in.MinReplicas, &out.MinReplicas - *out = new(int32) - **out = **in - } - if in.Metrics != nil { - in, out := &in.Metrics, &out.Metrics - *out = make([]v2beta1.MetricSpec, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonHpaSpec. -func (in *SeldonHpaSpec) DeepCopy() *SeldonHpaSpec { - if in == nil { - return nil - } - out := new(SeldonHpaSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SeldonPodSpec) DeepCopyInto(out *SeldonPodSpec) { - *out = *in - in.Metadata.DeepCopyInto(&out.Metadata) - in.Spec.DeepCopyInto(&out.Spec) - if in.HpaSpec != nil { - in, out := &in.HpaSpec, &out.HpaSpec - *out = new(SeldonHpaSpec) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SeldonPodSpec. -func (in *SeldonPodSpec) DeepCopy() *SeldonPodSpec { - if in == nil { - return nil - } - out := new(SeldonPodSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceStatus) DeepCopyInto(out *ServiceStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceStatus. -func (in *ServiceStatus) DeepCopy() *ServiceStatus { - if in == nil { - return nil - } - out := new(ServiceStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SvcOrchSpec) DeepCopyInto(out *SvcOrchSpec) { - *out = *in - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = new(v1.ResourceRequirements) - (*in).DeepCopyInto(*out) - } - if in.Env != nil { - in, out := &in.Env, &out.Env - *out = make([]*v1.EnvVar, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(v1.EnvVar) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SvcOrchSpec. -func (in *SvcOrchSpec) DeepCopy() *SvcOrchSpec { - if in == nil { - return nil - } - out := new(SvcOrchSpec) - in.DeepCopyInto(out) - return out -} diff --git a/operator/controllers/seldondeployment_controller.go b/operator/controllers/seldondeployment_controller.go index 5a0cb582f2..b00e55415e 100644 --- a/operator/controllers/seldondeployment_controller.go +++ b/operator/controllers/seldondeployment_controller.go @@ -593,7 +593,7 @@ func createPredictorService(pSvcName string, seldonId string, p *machinelearning // service for hitting a model directly, not via engine - not exposed externally, also adds probes func createContainerService(deploy *appsv1.Deployment, p machinelearningv1.PredictorSpec, mlDep *machinelearningv1.SeldonDeployment, con *corev1.Container, c components) *corev1.Service { containerServiceKey := machinelearningv1.GetPredictorServiceNameKey(con) - containerServiceValue := machinelearningv1.GetContainerServiceName(mlDep, p, con) + containerServiceValue := machinelearningv1.GetContainerServiceName(&mlDep.Spec, p, con) pu := machinelearningv1.GetPredictiveUnit(p.Graph, con.Name) // only create services for containers defined as pus in the graph