Skip to content

Commit

Permalink
Add tests and docs
Browse files Browse the repository at this point in the history
  • Loading branch information
ukclivecox committed Oct 12, 2019
1 parent 4e461b9 commit 70b33fb
Show file tree
Hide file tree
Showing 10 changed files with 966 additions and 54 deletions.
40 changes: 38 additions & 2 deletions doc/source/graph/svcorch.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The service orchestrator is a component that is added to your inference graph to

* Correctly manage the request/response paths described by your inference graph
* Expose prometheus metrics

* Add meta data to the response

## Resource Requests/Limits for Service Orchetsrator

Expand Down Expand Up @@ -67,4 +67,40 @@ The service orchestrator is a Java component. You can directly control its java
You can manipulate some of the functionality of the service orchestrator by adding specific environment variables to the `svcOrchSpec` section.

* [Configure Jaeger Tracing Example](../graph/distributed-tracing.html)
* [Set logging level in service orchestrator engine](../analytics/log_level.html#setting-log-level-in-the-seldon-engine)
* [Set logging level in service orchestrator engine](../analytics/log_level.html#setting-log-level-in-the-seldon-engine)

## Bypass Service Orchestrator (version >= 0.5.0, alpha feature)

If you are deploying a single model then for those wishing to minimize the network hops and have lowest latency and resource usage for their deployed model you can opt out of having the service orchestrator included. To do this add the annotation `seldon.io/no-engine: "true"` to the predictor. The predictor must contain just a single node graph. An example is shown below:


```YAML
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
labels:
app: seldon
name: noengine
spec:
name: noeng
predictors:
- annotations:
seldon.io/no-engine: "true"
componentSpecs:
- spec:
containers:
- image: seldonio/mock_classifier_rest:1.3
name: classifier
graph:
children: []
endpoint:
type: REST
name: classifier
type: MODEL
name: noeng
replicas: 1
```
In these cases the external API requests will be sent directly to your model. At present only the python wrapper (>=0.13-SNAPSHOT) has been modified to allow this.
Note no metrics or extra data will be added to the request so this would need to be done by your model itself if needed.
Binary file added notebooks/cat-raw.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions notebooks/resources/model_no_engine.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
apiVersion: machinelearning.seldon.io/v1alpha2
kind: SeldonDeployment
metadata:
labels:
app: seldon
name: noengine
spec:
name: noeng
predictors:
- annotations:
seldon.io/no-engine: "true"
componentSpecs:
- spec:
containers:
- image: seldonio/mock_classifier_rest:1.3
imagePullPolicy: Never
name: classifier
graph:
children: []
endpoint:
type: REST
name: classifier
type: MODEL
name: noeng
replicas: 1
87 changes: 87 additions & 0 deletions operator/controllers/noengine_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package controllers

import (
"context"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
machinelearningv1alpha2 "github.com/seldonio/seldon-core/operator/api/v1alpha2"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"time"
)

var _ = Describe("Create a Seldon Deployment without engine", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
By("Creating a resource")
It("should create a resource with defaults", func() {
Expect(k8sClient).NotTo(BeNil())
var modelType = machinelearningv1alpha2.MODEL
key := types.NamespacedName{
Name: "dep2",
Namespace: "default",
}
instance := &machinelearningv1alpha2.SeldonDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: key.Name,
Namespace: key.Namespace,
},
Spec: machinelearningv1alpha2.SeldonDeploymentSpec{
Name: "mydep2",
Predictors: []machinelearningv1alpha2.PredictorSpec{
{
Annotations: map[string]string{
"seldon.io/no-engine": "true",
},
Name: "p1",
ComponentSpecs: []*machinelearningv1alpha2.SeldonPodSpec{
{
Spec: v1.PodSpec{
Containers: []v1.Container{
{
Image: "seldonio/mock_classifier:1.0",
Name: "classifier",
},
},
},
},
},
Graph: &machinelearningv1alpha2.PredictiveUnit{
Name: "classifier",
Type: &modelType,
},
},
},
},
}

// Run Defaulter
instance.Default()

Expect(k8sClient.Create(context.Background(), instance)).Should(Succeed())
//time.Sleep(time.Second * 5)

fetched := &machinelearningv1alpha2.SeldonDeployment{}
Eventually(func() error {
err := k8sClient.Get(context.Background(), key, fetched)
return err
}, timeout, interval).Should(BeNil())
Expect(fetched.Spec.Name).Should(Equal("mydep2"))

depKey := types.NamespacedName{
Name: machinelearningv1alpha2.GetDeploymentName(instance, instance.Spec.Predictors[0], instance.Spec.Predictors[0].ComponentSpecs[0]),
Namespace: "default",
}
depFetched := &appsv1.Deployment{}
Eventually(func() error {
err := k8sClient.Get(context.Background(), depKey, depFetched)
return err
}, timeout, interval).Should(BeNil())
Expect(len(depFetched.Spec.Template.Spec.Containers)).Should(Equal(1))

Expect(k8sClient.Delete(context.Background(), instance)).Should(Succeed())
})

})
68 changes: 20 additions & 48 deletions operator/controllers/seldondeployment_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,21 @@ import (
. "github.com/onsi/gomega"
machinelearningv1alpha2 "github.com/seldonio/seldon-core/operator/api/v1alpha2"
"io/ioutil"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"path/filepath"
"time"
)

const timeout = time.Second * 5

func helperLoadBytes(name string) []byte {
path := filepath.Join("testdata", name) // relative path
bytes, _ := ioutil.ReadFile(path)
return bytes
}

var _ = Describe("Create a deployment", func() {
var _ = Describe("Create a Seldon Deployment", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
By("Creating a resource")
Expand Down Expand Up @@ -78,8 +77,12 @@ var _ = Describe("Create a deployment", func() {
},
},
}

// Run Defaulter
instance.Default()

Expect(k8sClient.Create(context.Background(), instance)).Should(Succeed())
time.Sleep(time.Second * 5)
//time.Sleep(time.Second * 5)

fetched := &machinelearningv1alpha2.SeldonDeployment{}
Eventually(func() error {
Expand All @@ -88,50 +91,19 @@ var _ = Describe("Create a deployment", func() {
}, timeout, interval).Should(BeNil())
Expect(fetched.Spec.Name).Should(Equal("mydep"))

})

})

func coreDeploymentTests(instance *machinelearningv1alpha2.SeldonDeployment) {

// Create the SeldonDeployment object and expect the Reconcile and Deployment to be created
Expect(k8sClient.Create(context.Background(), instance)).Should(Succeed())
time.Sleep(time.Second * 5)
err := k8sClient.Create(context.TODO(), instance)

Expect(err).NotTo(HaveOccurred())
// delete the SeldonDeployment at end of test
defer k8sClient.Delete(context.TODO(), instance)

}

/*
func testReconcileSimpleModel(t *testing.T) {
instance := &machinelearningv1alpha2.SeldonDeployment{}
bStr := helperLoadBytes("model.json")
json.Unmarshal(bStr, instance)
coreDeploymentTests(instance)
}
func testReconcileModelLongName(t *testing.T) {
instance := &machinelearningv1alpha2.SeldonDeployment{}
bStr := helperLoadBytes("model_long_name.json")
json.Unmarshal(bStr, instance)
coreDeploymentTests(instance)
}
func testReconcileHpaModel(t *testing.T) {
instance := &machinelearningv1alpha2.SeldonDeployment{}
depKey := types.NamespacedName{
Name: machinelearningv1alpha2.GetDeploymentName(instance, instance.Spec.Predictors[0], instance.Spec.Predictors[0].ComponentSpecs[0]),
Namespace: "default",
}
depFetched := &appsv1.Deployment{}
Eventually(func() error {
err := k8sClient.Get(context.Background(), depKey, depFetched)
return err
}, timeout, interval).Should(BeNil())
Expect(len(depFetched.Spec.Template.Spec.Containers)).Should(Equal(2))

bStr := helperLoadBytes("model_with_hpa.json")
json.Unmarshal(bStr, instance)
Expect(k8sClient.Delete(context.Background(), instance)).Should(Succeed())

coreDeploymentTests(instance)
}
})

*/
})
77 changes: 77 additions & 0 deletions operator/controllers/seldondeployment_prepackaged_servers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package controllers

import (
"context"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
machinelearningv1alpha2 "github.com/seldonio/seldon-core/operator/api/v1alpha2"
"github.com/seldonio/seldon-core/operator/utils"
appsv1 "k8s.io/api/apps/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"time"
)

var _ = Describe("Create a prepacked sklearn server", func() {
const timeout = time.Second * 30
const interval = time.Second * 1
By("Creating a resource")
It("should create a resource with defaults", func() {
Expect(k8sClient).NotTo(BeNil())
var modelType = machinelearningv1alpha2.MODEL
var impl = machinelearningv1alpha2.SKLEARN_SERVER
key := types.NamespacedName{
Name: "prepack",
Namespace: "default",
}
instance := &machinelearningv1alpha2.SeldonDeployment{
ObjectMeta: metav1.ObjectMeta{
Name: key.Name,
Namespace: key.Namespace,
},
Spec: machinelearningv1alpha2.SeldonDeploymentSpec{
Name: "pp",
Predictors: []machinelearningv1alpha2.PredictorSpec{
{
Name: "p1",
Graph: &machinelearningv1alpha2.PredictiveUnit{
Name: "classifier",
Type: &modelType,
Implementation: &impl,
Endpoint: &machinelearningv1alpha2.Endpoint{Type: machinelearningv1alpha2.REST},
},
},
},
},
}

// Run Defaulter
instance.Default()

Expect(k8sClient.Create(context.Background(), instance)).Should(Succeed())
//time.Sleep(time.Second * 5)

fetched := &machinelearningv1alpha2.SeldonDeployment{}
Eventually(func() error {
err := k8sClient.Get(context.Background(), key, fetched)
return err
}, timeout, interval).Should(BeNil())
Expect(fetched.Spec.Name).Should(Equal("pp"))

sPodSpec := utils.GetSeldonPodSpecForPredictiveUnit(&instance.Spec.Predictors[0], instance.Spec.Predictors[0].Graph.Name)
depName := machinelearningv1alpha2.GetDeploymentName(instance, instance.Spec.Predictors[0], sPodSpec)
depKey := types.NamespacedName{
Name: depName,
Namespace: "default",
}
depFetched := &appsv1.Deployment{}
Eventually(func() error {
err := k8sClient.Get(context.Background(), depKey, depFetched)
return err
}, timeout, interval).Should(BeNil())
Expect(len(depFetched.Spec.Template.Spec.Containers)).Should(Equal(2))

Expect(k8sClient.Delete(context.Background(), instance)).Should(Succeed())
})

})
Loading

0 comments on commit 70b33fb

Please sign in to comment.