Skip to content

Commit

Permalink
update endpoint status (kubeflow#388)
Browse files Browse the repository at this point in the history
* use endpoint status map

* patch tests

* update codeget

* make the map a ptr

* fix TestKFServiceWithOnlyPredictor

* fix canary predictor test

* fix TestKFServiceWithOnlyPredictor with sad sleep

* update printer columns

* update codegen and move url up
  • Loading branch information
rakelkar authored and k8s-ci-robot committed Oct 2, 2019
1 parent a492913 commit 90cf79f
Show file tree
Hide file tree
Showing 11 changed files with 543 additions and 323 deletions.
28 changes: 5 additions & 23 deletions config/default/crds/serving_v1alpha2_kfservice.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ metadata:
name: kfservices.serving.kubeflow.org
spec:
additionalPrinterColumns:
- JSONPath: .status.conditions[?(@.type=='Ready')].status
name: Ready
type: string
- JSONPath: .status.url
name: URL
type: string
- JSONPath: .status.default.traffic
- JSONPath: .status.conditions[?(@.type=='Ready')].status
name: Ready
type: string
- JSONPath: .status.default.predictor.traffic
name: Default Traffic
type: integer
- JSONPath: .status.canary.traffic
- JSONPath: .status.canary.predictor.traffic
name: Canary Traffic
type: integer
- JSONPath: .metadata.creationTimestamp
Expand Down Expand Up @@ -454,15 +454,6 @@ spec:
status:
properties:
canary:
properties:
name:
type: string
replicas:
format: int64
type: integer
traffic:
format: int64
type: integer
type: object
conditions:
description: Conditions the latest available observations of a resource's
Expand Down Expand Up @@ -499,15 +490,6 @@ spec:
type: object
type: array
default:
properties:
name:
type: string
replicas:
format: int64
type: integer
traffic:
format: int64
type: integer
type: object
observedGeneration:
description: ObservedGeneration is the 'Generation' of the Service that
Expand Down
124 changes: 78 additions & 46 deletions pkg/apis/serving/v1alpha2/kfservice_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,36 @@ import (
const (
// RoutesReady is set when network configuration has completed.
RoutesReady apis.ConditionType = "RoutesReady"
// PredictRoutesReady is set when network configuration has completed for predict endpoint verb.
PredictRoutesReady apis.ConditionType = "PredictRoutesReady"
// ExplainRoutesReady is set when network configuration has completed for explain endpoint verb.
ExplainRoutesReady apis.ConditionType = "ExplainRoutesReady"
// DefaultPredictorReady is set when default predictor has reported readiness.
DefaultPredictorReady apis.ConditionType = "DefaultPredictorReady"
// CanaryPredictorReady is set when canary predictor has reported readiness.
CanaryPredictorReady apis.ConditionType = "CanaryPredictorReady"
// DefaultExplainerReady is set when default explainer has reported readiness.
DefaultExplainerReady apis.ConditionType = "DefaultExplainerReady"
// CanaryExplainerReady is set when canary explainer has reported readiness.
CanaryExplainerReady apis.ConditionType = "CanaryExplainerReady"
// DefaultTransformerReady is set when default transformer has reported readiness.
DefaultTransformerReady apis.ConditionType = "DefaultTransformerReady"
// CanaryTransformerReady is set when canary transformer has reported readiness.
CanaryTransformerReady apis.ConditionType = "CanaryTransformerReady"
)

var defaultConditionsMap = map[constants.KFServiceEndpoint]apis.ConditionType{
constants.Predictor: DefaultPredictorReady,
constants.Explainer: DefaultExplainerReady,
constants.Transformer: DefaultTransformerReady,
}

var canaryConditionsMap = map[constants.KFServiceEndpoint]apis.ConditionType{
constants.Predictor: CanaryPredictorReady,
constants.Explainer: CanaryExplainerReady,
constants.Transformer: CanaryTransformerReady,
}

// KFService Ready condition is depending on default predictor and route readiness condition
// canary readiness condition only present when canary is used and currently does
// not affect KFService readiness condition.
Expand All @@ -54,77 +78,72 @@ func (ss *KFServiceStatus) GetCondition(t apis.ConditionType) *apis.Condition {
return conditionSet.Manage(ss).GetCondition(t)
}

// PropagateDefaultStatus propagates the status for the default spec
func (ss *KFServiceStatus) PropagateDefaultStatus(endpoint constants.KFServiceEndpoint, defaultStatus *knservingv1alpha1.ServiceStatus) {
switch endpoint {
case constants.Predictor:
ss.PropagateDefaultPredictorStatus(defaultStatus)
case constants.Explainer:
case constants.Transformer:
if ss.Default == nil {
emptyStatusMap := make(EndpointStatusMap)
ss.Default = &emptyStatusMap
}
conditionType := defaultConditionsMap[endpoint]
statusSpec, ok := (*ss.Default)[endpoint]
if !ok {
statusSpec = &StatusConfigurationSpec{}
(*ss.Default)[endpoint] = statusSpec
}
ss.propagateStatus(statusSpec, conditionType, defaultStatus)
}

// PropagateCanaryStatus propagates the status for the canary spec
func (ss *KFServiceStatus) PropagateCanaryStatus(endpoint constants.KFServiceEndpoint, canaryStatus *knservingv1alpha1.ServiceStatus) {
switch endpoint {
case constants.Predictor:
ss.PropagateCanaryPredictorStatus(canaryStatus)
case constants.Explainer:
case constants.Transformer:

conditionType := canaryConditionsMap[endpoint]

// reset status if canaryServiceStatus is nil
if canaryStatus == nil {
emptyStatusMap := make(EndpointStatusMap)
ss.Canary = &emptyStatusMap
conditionSet.Manage(ss).ClearCondition(conditionType)
return
}
}

// PropagateDefaultPredictorStatus propagates the default predictor status and applies its values
// to the Service status.
func (ss *KFServiceStatus) PropagateDefaultPredictorStatus(defaultStatus *knservingv1alpha1.ServiceStatus) {
ss.Default.Name = defaultStatus.LatestCreatedRevisionName
serviceCondition := defaultStatus.GetCondition(knservingv1alpha1.ServiceConditionReady)
if ss.Canary == nil {
emptyStatusMap := make(EndpointStatusMap)
ss.Canary = &emptyStatusMap
}

switch {
case serviceCondition == nil:
case serviceCondition.Status == v1.ConditionUnknown:
conditionSet.Manage(ss).MarkUnknown(DefaultPredictorReady, serviceCondition.Reason, serviceCondition.Message)
case serviceCondition.Status == v1.ConditionTrue:
conditionSet.Manage(ss).MarkTrue(DefaultPredictorReady)
case serviceCondition.Status == v1.ConditionFalse:
conditionSet.Manage(ss).MarkFalse(DefaultPredictorReady, serviceCondition.Reason, serviceCondition.Message)
statusSpec, ok := (*ss.Canary)[endpoint]
if !ok {
statusSpec = &StatusConfigurationSpec{}
(*ss.Canary)[endpoint] = statusSpec
}

ss.propagateStatus(statusSpec, conditionType, canaryStatus)
}

// PropagateCanaryPredictorStatus propagates the canary predictor status and applies its values
// to the Service status.
func (ss *KFServiceStatus) PropagateCanaryPredictorStatus(canaryStatus *knservingv1alpha1.ServiceStatus) {
// reset status if canaryServiceStatus is nil
if canaryStatus == nil {
ss.Canary = StatusConfigurationSpec{}
conditionSet.Manage(ss).ClearCondition(CanaryPredictorReady)
return
func (ss *KFServiceStatus) propagateStatus(statusSpec *StatusConfigurationSpec, conditionType apis.ConditionType, serviceStatus *knservingv1alpha1.ServiceStatus) {
statusSpec.Name = serviceStatus.LatestCreatedRevisionName
if serviceStatus.URL != nil {
statusSpec.Hostname = serviceStatus.URL.Host
}
ss.Canary.Name = canaryStatus.LatestCreatedRevisionName
serviceCondition := canaryStatus.GetCondition(knservingv1alpha1.ServiceConditionReady)

serviceCondition := serviceStatus.GetCondition(knservingv1alpha1.ServiceConditionReady)
switch {
case serviceCondition == nil:
case serviceCondition.Status == v1.ConditionUnknown:
conditionSet.Manage(ss).MarkUnknown(CanaryPredictorReady, serviceCondition.Reason, serviceCondition.Message)
conditionSet.Manage(ss).MarkUnknown(conditionType, serviceCondition.Reason, serviceCondition.Message)
case serviceCondition.Status == v1.ConditionTrue:
conditionSet.Manage(ss).MarkTrue(CanaryPredictorReady)
conditionSet.Manage(ss).MarkTrue(conditionType)
case serviceCondition.Status == v1.ConditionFalse:
conditionSet.Manage(ss).MarkFalse(CanaryPredictorReady, serviceCondition.Reason, serviceCondition.Message)
conditionSet.Manage(ss).MarkFalse(conditionType, serviceCondition.Reason, serviceCondition.Message)
}
}

// PropagateRouteStatus propagates route's status to the service's status.
func (ss *KFServiceStatus) PropagateRouteStatus(rs *knservingv1alpha1.RouteStatus) {
ss.URL = rs.URL.String()

for _, traffic := range rs.Traffic {
switch traffic.RevisionName {
case ss.Default.Name:
ss.Default.Traffic = traffic.Percent
case ss.Canary.Name:
ss.Canary.Traffic = traffic.Percent
default:
}
}
propagateRouteStatus(rs, ss.Default)
propagateRouteStatus(rs, ss.Canary)

rc := rs.GetCondition(knservingv1alpha1.RouteConditionReady)

Expand All @@ -138,3 +157,16 @@ func (ss *KFServiceStatus) PropagateRouteStatus(rs *knservingv1alpha1.RouteStatu
conditionSet.Manage(ss).MarkFalse(RoutesReady, rc.Reason, rc.Message)
}
}

func propagateRouteStatus(rs *knservingv1alpha1.RouteStatus, endpointStatusMap *EndpointStatusMap) {
for _, traffic := range rs.Traffic {
for _, endpoint := range *endpointStatusMap {
if endpoint.Name == traffic.RevisionName {
endpoint.Traffic = traffic.Percent
if traffic.URL != nil {
endpoint.Hostname = traffic.URL.Host
}
}
}
}
}
5 changes: 3 additions & 2 deletions pkg/apis/serving/v1alpha2/kfservice_status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1alpha2

import (
"github.com/kubeflow/kfserving/pkg/constants"
"k8s.io/api/core/v1"
"knative.dev/pkg/apis/duck"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
Expand Down Expand Up @@ -196,8 +197,8 @@ func TestKFServiceIsReady(t *testing.T) {
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
status := KFServiceStatus{}
status.PropagateDefaultPredictorStatus(&tc.defaultServiceStatus)
status.PropagateCanaryPredictorStatus(&tc.canaryServiceStatus)
status.PropagateDefaultStatus(constants.Predictor, &tc.defaultServiceStatus)
status.PropagateCanaryStatus(constants.Predictor, &tc.canaryServiceStatus)
status.PropagateRouteStatus(&tc.routeStatus)
if e, a := tc.isReady, status.IsReady(); e != a {
t.Errorf("%q expected: %v got: %v", tc.name, e, a)
Expand Down
17 changes: 11 additions & 6 deletions pkg/apis/serving/v1alpha2/kfservice_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ limitations under the License.
package v1alpha2

import (
"github.com/kubeflow/kfserving/pkg/constants"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
duckv1beta1 "knative.dev/pkg/apis/duck/v1beta1"
Expand Down Expand Up @@ -182,17 +183,21 @@ type CustomSpec struct {
Container v1.Container `json:"container"`
}

// EndpointStatusMap defines the observed state of KFService endpoints
type EndpointStatusMap map[constants.KFServiceEndpoint]*StatusConfigurationSpec

// KFServiceStatus defines the observed state of KFService
type KFServiceStatus struct {
duckv1beta1.Status `json:",inline"`
URL string `json:"url,omitempty"`
Default StatusConfigurationSpec `json:"default,omitempty"`
Canary StatusConfigurationSpec `json:"canary,omitempty"`
URL string `json:"url,omitempty"`
Default *EndpointStatusMap `json:"default,omitempty"`
Canary *EndpointStatusMap `json:"canary,omitempty"`
}

// StatusConfigurationSpec describes the state of the configuration receiving traffic.
type StatusConfigurationSpec struct {
Name string `json:"name,omitempty"`
Hostname string `json:"host,omitempty"`
Replicas int `json:"replicas,omitempty"`
Traffic int `json:"traffic,omitempty"`
}
Expand All @@ -202,10 +207,10 @@ type StatusConfigurationSpec struct {

// KFService is the Schema for the services API
// +k8s:openapi-gen=true
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
// +kubebuilder:printcolumn:name="URL",type="string",JSONPath=".status.url"
// +kubebuilder:printcolumn:name="Default Traffic",type="integer",JSONPath=".status.default.traffic"
// +kubebuilder:printcolumn:name="Canary Traffic",type="integer",JSONPath=".status.canary.traffic"
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status"
// +kubebuilder:printcolumn:name="Default Traffic",type="integer",JSONPath=".status.default.predictor.traffic"
// +kubebuilder:printcolumn:name="Canary Traffic",type="integer",JSONPath=".status.canary.predictor.traffic"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:resource:path=kfservices,shortName=kfservice
type KFService struct {
Expand Down
21 changes: 13 additions & 8 deletions pkg/apis/serving/v1alpha2/kfservice_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package v1alpha2

import (
"github.com/kubeflow/kfserving/pkg/constants"
"testing"

"github.com/onsi/gomega"
Expand Down Expand Up @@ -84,15 +85,19 @@ func TestKFService(t *testing.T) {
statusUpdated := fetched.DeepCopy()
statusUpdated.Status = KFServiceStatus{
URL: "example.dev.com",
Default: StatusConfigurationSpec{
Name: "v1",
Traffic: 20,
Replicas: 2,
Default: &EndpointStatusMap{
constants.Predictor: &StatusConfigurationSpec{
Name: "v1",
Traffic: 20,
Replicas: 2,
},
},
Canary: StatusConfigurationSpec{
Name: "v2",
Traffic: 80,
Replicas: 3,
Canary: &EndpointStatusMap{
constants.Predictor: &StatusConfigurationSpec{
Name: "v2",
Traffic: 80,
Replicas: 3,
},
},
}
g.Expect(c.Update(context.TODO(), statusUpdated)).NotTo(gomega.HaveOccurred())
Expand Down
Loading

0 comments on commit 90cf79f

Please sign in to comment.