Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move the Service controller to use the new controller model. #1208

Merged
merged 1 commit into from
Jun 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions pkg/apis/serving/v1alpha1/revision_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,13 +398,12 @@ func (rs *RevisionStatus) MarkContainerMissing(message string) {
}

func (rs *RevisionStatus) checkAndMarkReady() {
ra := rs.GetCondition(RevisionConditionResourcesAvailable)
if ra == nil || ra.Status != corev1.ConditionTrue {
return
}
ch := rs.GetCondition(RevisionConditionContainerHealthy)
if ch == nil || ch.Status != corev1.ConditionTrue {
return
rct := []RevisionConditionType{RevisionConditionContainerHealthy, RevisionConditionResourcesAvailable}
for _, cond := range rct {
c := rs.GetCondition(cond)
if c == nil || c.Status != corev1.ConditionTrue {
return
}
}
rs.markReady()
}
Expand Down
73 changes: 72 additions & 1 deletion pkg/apis/serving/v1alpha1/service_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,12 @@ const (
// ServiceConditionReady is set when the service is configured
// and has available backends ready to receive traffic.
ServiceConditionReady ServiceConditionType = "Ready"
// ServiceConditionRouteReady is set when the service's underlying
// route has reported readiness.
ServiceConditionRouteReady ServiceConditionType = "RouteReady"
// ServiceConditionConfigurationReady is set when the service's underlying
// configuration has reported readiness.
ServiceConditionConfigurationReady ServiceConditionType = "ConfigurationReady"
)

type ServiceStatus struct {
Expand Down Expand Up @@ -183,7 +189,8 @@ func (ss *ServiceStatus) RemoveCondition(t ServiceConditionType) {
}

func (ss *ServiceStatus) InitializeConditions() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not opposed to this solution, but seems like defaulter-gen might be cleaner. If only defaulter-gen were documented...

sct := []ServiceConditionType{ServiceConditionReady}
sct := []ServiceConditionType{ServiceConditionReady,
ServiceConditionConfigurationReady, ServiceConditionRouteReady}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can each of these be on a new line - for when we add new conditions in the future

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will push a change to normalize to that style across *_types.go

for _, cond := range sct {
if rc := ss.GetCondition(cond); rc == nil {
ss.setCondition(&ServiceCondition{
Expand All @@ -193,3 +200,67 @@ func (ss *ServiceStatus) InitializeConditions() {
}
}
}

func (ss *ServiceStatus) PropagateConfiguration(cs ConfigurationStatus) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer PropagateConfigurationStatus

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will push a change with this rename.

cc := cs.GetCondition(ConfigurationConditionReady)
if cc == nil {
return
}
sct := []ServiceConditionType{ServiceConditionConfigurationReady}
// If the underlying Configuration reported failure, then bubble it up.
if cc.Status == corev1.ConditionFalse {
sct = append(sct, ServiceConditionReady)
}
for _, cond := range sct {
ss.setCondition(&ServiceCondition{
Type: cond,
Status: cc.Status,
Reason: cc.Reason,
Message: cc.Message,
})
}
if cc.Status == corev1.ConditionTrue {
ss.checkAndMarkReady()
}
}

func (ss *ServiceStatus) PropagateRoute(rs RouteStatus) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer PropagateRouteStatus

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will push a change with this rename.

rc := rs.GetCondition(RouteConditionReady)
if rc == nil {
return
}
sct := []ServiceConditionType{ServiceConditionRouteReady}
// If the underlying Route reported failure, then bubble it up.
if rc.Status == corev1.ConditionFalse {
sct = append(sct, ServiceConditionReady)
}
for _, cond := range sct {
ss.setCondition(&ServiceCondition{
Type: cond,
Status: rc.Status,
Reason: rc.Reason,
Message: rc.Message,
})
}
if rc.Status == corev1.ConditionTrue {
ss.checkAndMarkReady()
}
}

func (ss *ServiceStatus) checkAndMarkReady() {
sct := []ServiceConditionType{ServiceConditionConfigurationReady, ServiceConditionRouteReady}
for _, cond := range sct {
c := ss.GetCondition(cond)
if c == nil || c.Status != corev1.ConditionTrue {
return
}
}
ss.markReady()
}

func (ss *ServiceStatus) markReady() {
ss.setCondition(&ServiceCondition{
Type: ServiceConditionReady,
Status: corev1.ConditionTrue,
})
}
113 changes: 109 additions & 4 deletions pkg/apis/serving/v1alpha1/service_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,116 @@ func TestServiceConditions(t *testing.T) {
}

func TestTypicalServiceFlow(t *testing.T) {
r := &Service{}
r.Status.InitializeConditions()
checkConditionOngoingService(r.Status, ServiceConditionReady, t)
svc := &Service{}
svc.Status.InitializeConditions()
checkConditionOngoingService(svc.Status, ServiceConditionReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionRouteReady, t)

// Nothing from Configuration is nothing to us.
svc.Status.PropagateConfiguration(ConfigurationStatus{})
checkConditionOngoingService(svc.Status, ServiceConditionReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionRouteReady, t)

// Nothing from Route is nothing to us.
svc.Status.PropagateRoute(RouteStatus{})
checkConditionOngoingService(svc.Status, ServiceConditionReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionRouteReady, t)

// Done from Configuration moves our ConfigurationReady condition
svc.Status.PropagateConfiguration(ConfigurationStatus{
Conditions: []ConfigurationCondition{{
Type: ConfigurationConditionReady,
Status: corev1.ConditionTrue,
}},
})
checkConditionOngoingService(svc.Status, ServiceConditionReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionRouteReady, t)

// Done from Route moves our RouteReady condition, which triggers us to be Ready.
svc.Status.PropagateRoute(RouteStatus{
Conditions: []RouteCondition{{
Type: RouteConditionReady,
Status: corev1.ConditionTrue,
}},
})
checkConditionSucceededService(svc.Status, ServiceConditionReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionRouteReady, t)

// Check idempotency
svc.Status.PropagateRoute(RouteStatus{
Conditions: []RouteCondition{{
Type: RouteConditionReady,
Status: corev1.ConditionTrue,
}},
})
checkConditionSucceededService(svc.Status, ServiceConditionReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionRouteReady, t)

// Failure causes us to become unready immediately (config still ok).
svc.Status.PropagateRoute(RouteStatus{
Conditions: []RouteCondition{{
Type: RouteConditionReady,
Status: corev1.ConditionFalse,
}},
})
checkConditionFailedService(svc.Status, ServiceConditionReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionFailedService(svc.Status, ServiceConditionRouteReady, t)

// Fixed the glitch.
svc.Status.PropagateRoute(RouteStatus{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest more granularity and test the scenarios below. Let me know if it's too pedantic. I'm suggesting this because there's no test for when a configuration fails and recovers. And there's some overlap testing the route failure flow in TestTypicalServiceFlow and TestRouteFailurePropagation

Each bullet point would be a scenario to test given the conditions when ...

service happy flow

when the service ready condition is unknown

  • route ready condition becomes true
  • configuration ready condition becomes true
  • both route and configuration ready condition becomes true

failure propagation

when the service ready condition is true
and both route & configuration ready conditions are true

  • route ready condition becomes false
  • configuration ready condition becomes false

configuration recovery

when the service ready condition is false
and route ready condition is true
and configuration ready condition is false

  • configuration ready condition becomes true

route recovery

when the service ready condition is false
and route ready condition is false
and configuration ready condition is true

  • route ready condition becomes true

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened #1224 to track this. This is a great list, thanks.

Conditions: []RouteCondition{{
Type: RouteConditionReady,
Status: corev1.ConditionTrue,
}},
})
checkConditionSucceededService(svc.Status, ServiceConditionReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionSucceededService(svc.Status, ServiceConditionRouteReady, t)
}

func TestConfigurationFailurePropagation(t *testing.T) {
svc := &Service{}
svc.Status.InitializeConditions()
checkConditionOngoingService(svc.Status, ServiceConditionReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionRouteReady, t)

// Failure causes us to become unready immediately
svc.Status.PropagateConfiguration(ConfigurationStatus{
Conditions: []ConfigurationCondition{{
Type: ConfigurationConditionReady,
Status: corev1.ConditionFalse,
}},
})
checkConditionFailedService(svc.Status, ServiceConditionReady, t)
checkConditionFailedService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionRouteReady, t)
}

func TestRouteFailurePropagation(t *testing.T) {
svc := &Service{}
svc.Status.InitializeConditions()
checkConditionOngoingService(svc.Status, ServiceConditionReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionRouteReady, t)

// TODO(#1134): Service needs proper conditions.
// Failure causes us to become unready immediately
svc.Status.PropagateRoute(RouteStatus{
Conditions: []RouteCondition{{
Type: RouteConditionReady,
Status: corev1.ConditionFalse,
}},
})
checkConditionFailedService(svc.Status, ServiceConditionReady, t)
checkConditionOngoingService(svc.Status, ServiceConditionConfigurationReady, t)
checkConditionFailedService(svc.Status, ServiceConditionRouteReady, t)
}

func checkConditionSucceededService(rs ServiceStatus, rct ServiceConditionType, t *testing.T) *ServiceCondition {
Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type Controller struct {

buildClientSet buildclientset.Interface

// lister indexes properties about Configuration
// listers index properties about resources
lister listers.ConfigurationLister
revisionLister listers.RevisionLister
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/controller/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ func GetElaK8SServiceName(u *v1alpha1.Route) string {
return u.Name + "-service"
}

func GetServiceConfigurationName(u *v1alpha1.Service) string {
return u.Name
}

func GetServiceRouteName(u *v1alpha1.Service) string {
return u.Name
}

func GetElaK8SActivatorServiceName() string {
return "activator-service"
}
Expand Down
Loading