Skip to content

Commit b93a5d5

Browse files
author
Jim Minter
committed
implement template completion detection
1 parent 20c03cc commit b93a5d5

File tree

11 files changed

+1379
-59
lines changed

11 files changed

+1379
-59
lines changed

hack/update-generated-bindata.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pushd "${OS_ROOT}" > /dev/null
5252
examples/prometheus/... \
5353
examples/hello-openshift \
5454
examples/jenkins/... \
55+
examples/quickstarts/cakephp-mysql.json \
5556
examples/templateservicebroker/...
5657

5758
popd > /dev/null

pkg/template/apis/template/constants.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,9 @@ const (
5656
// Base64ExposeAnnotationPrefix is as ExposeAnnotationPrefix, except that
5757
// any []byte values returned are base64 encoded.
5858
Base64ExposeAnnotationPrefix = "template.openshift.io/base64-expose-"
59+
60+
// WaitForReadyAnnotation indicates that the TemplateInstance controller
61+
// should wait for the object to be ready before reporting the template
62+
// instantiation complete.
63+
WaitForReadyAnnotation = "template.alpha.openshift.io/wait-for-ready"
5964
)

pkg/template/apis/template/helpers.go

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66

7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
78
"k8s.io/apimachinery/pkg/runtime"
89
"k8s.io/apimachinery/pkg/runtime/schema"
910
kapi "k8s.io/kubernetes/pkg/api"
@@ -47,16 +48,30 @@ func AddObjectsToTemplate(template *Template, objects []runtime.Object, targetVe
4748
return nil
4849
}
4950

50-
// FilterTemplateInstanceCondition returns a new []TemplateInstanceCondition,
51-
// ensuring that it does not contain conditions of condType.
52-
func FilterTemplateInstanceCondition(conditions []TemplateInstanceCondition, condType TemplateInstanceConditionType) []TemplateInstanceCondition {
53-
newConditions := make([]TemplateInstanceCondition, 0, len(conditions)+1)
51+
func (templateInstance *TemplateInstance) HasCondition(typ TemplateInstanceConditionType, status kapi.ConditionStatus) bool {
52+
for _, c := range templateInstance.Status.Conditions {
53+
if c.Type == typ && c.Status == status {
54+
return true
55+
}
56+
}
57+
return false
58+
}
59+
60+
func (templateInstance *TemplateInstance) SetCondition(condition TemplateInstanceCondition) {
61+
condition.LastTransitionTime = metav1.Now()
62+
63+
for i, c := range templateInstance.Status.Conditions {
64+
if c.Type == condition.Type {
65+
if c.Message == condition.Message &&
66+
c.Reason == condition.Reason &&
67+
c.Status == condition.Status {
68+
return
69+
}
5470

55-
for _, c := range conditions {
56-
if c.Type != condType {
57-
newConditions = append(newConditions, c)
71+
templateInstance.Status.Conditions[i] = condition
72+
return
5873
}
5974
}
6075

61-
return newConditions
76+
templateInstance.Status.Conditions = append(templateInstance.Status.Conditions, condition)
6277
}

pkg/template/controller/readiness.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package controller
2+
3+
import (
4+
"strconv"
5+
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
"k8s.io/apimachinery/pkg/runtime"
8+
"k8s.io/apimachinery/pkg/runtime/schema"
9+
kapi "k8s.io/kubernetes/pkg/api"
10+
"k8s.io/kubernetes/pkg/apis/apps"
11+
"k8s.io/kubernetes/pkg/apis/batch"
12+
"k8s.io/kubernetes/pkg/apis/extensions"
13+
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
14+
15+
buildapi "github.com/openshift/origin/pkg/build/apis/build"
16+
buildutil "github.com/openshift/origin/pkg/build/util"
17+
"github.com/openshift/origin/pkg/client"
18+
deployapi "github.com/openshift/origin/pkg/deploy/apis/apps"
19+
)
20+
21+
// checkBuildReadiness determins if a Build is ready, failed or neither.
22+
func checkBuildReadiness(oc client.Interface, obj runtime.Object) (bool, bool, error) {
23+
b := obj.(*buildapi.Build)
24+
25+
ready := buildutil.IsTerminalPhase(b.Status.Phase) &&
26+
b.Status.Phase == buildapi.BuildPhaseComplete
27+
28+
failed := buildutil.IsTerminalPhase(b.Status.Phase) &&
29+
b.Status.Phase != buildapi.BuildPhaseComplete
30+
31+
return ready, failed, nil
32+
}
33+
34+
// checkBuildConfigReadiness determins if a BuildConfig is ready, failed or
35+
// neither. TODO: this should be reported on the BuildConfig object itself.
36+
func checkBuildConfigReadiness(oc client.Interface, obj runtime.Object) (bool, bool, error) {
37+
bc := obj.(*buildapi.BuildConfig)
38+
39+
builds, err := oc.Builds(bc.Namespace).List(metav1.ListOptions{LabelSelector: buildutil.BuildConfigSelector(bc.Name).String()})
40+
if err != nil {
41+
return false, false, err
42+
}
43+
44+
for _, build := range builds.Items {
45+
if build.Annotations[buildapi.BuildNumberAnnotation] == strconv.FormatInt(bc.Status.LastVersion, 10) {
46+
return checkBuildReadiness(oc, &build)
47+
}
48+
}
49+
50+
return false, false, nil
51+
}
52+
53+
// checkDeploymentReadiness determins if a Deployment is ready, failed or
54+
// neither.
55+
func checkDeploymentReadiness(oc client.Interface, obj runtime.Object) (bool, bool, error) {
56+
d := obj.(*extensions.Deployment)
57+
58+
var progressing, available *extensions.DeploymentCondition
59+
for i, condition := range d.Status.Conditions {
60+
switch condition.Type {
61+
case extensions.DeploymentProgressing:
62+
progressing = &d.Status.Conditions[i]
63+
64+
case extensions.DeploymentAvailable:
65+
available = &d.Status.Conditions[i]
66+
}
67+
}
68+
69+
ready := d.Status.ObservedGeneration == d.Generation &&
70+
progressing != nil &&
71+
progressing.Status == kapi.ConditionTrue &&
72+
progressing.Reason == deploymentutil.NewRSAvailableReason &&
73+
available != nil &&
74+
available.Status == kapi.ConditionTrue
75+
76+
failed := d.Status.ObservedGeneration == d.Generation &&
77+
progressing != nil &&
78+
progressing.Status == kapi.ConditionFalse
79+
80+
return ready, failed, nil
81+
}
82+
83+
// checkDeploymentConfigReadiness determins if a DeploymentConfig is ready,
84+
// failed or neither.
85+
func checkDeploymentConfigReadiness(oc client.Interface, obj runtime.Object) (bool, bool, error) {
86+
dc := obj.(*deployapi.DeploymentConfig)
87+
88+
var progressing, available *deployapi.DeploymentCondition
89+
for i, condition := range dc.Status.Conditions {
90+
switch condition.Type {
91+
case deployapi.DeploymentProgressing:
92+
progressing = &dc.Status.Conditions[i]
93+
94+
case deployapi.DeploymentAvailable:
95+
available = &dc.Status.Conditions[i]
96+
}
97+
}
98+
99+
ready := dc.Status.ObservedGeneration == dc.Generation &&
100+
progressing != nil &&
101+
progressing.Status == kapi.ConditionTrue &&
102+
progressing.Reason == deployapi.NewRcAvailableReason &&
103+
available != nil &&
104+
available.Status == kapi.ConditionTrue
105+
106+
failed := dc.Status.ObservedGeneration == dc.Generation &&
107+
progressing != nil &&
108+
progressing.Status == kapi.ConditionFalse
109+
110+
return ready, failed, nil
111+
}
112+
113+
// checkJobReadiness determins if a Job is ready, failed or neither.
114+
func checkJobReadiness(oc client.Interface, obj runtime.Object) (bool, bool, error) {
115+
job := obj.(*batch.Job)
116+
117+
ready := job.Status.CompletionTime != nil
118+
failed := job.Status.Failed > 0
119+
120+
return ready, failed, nil
121+
}
122+
123+
// checkStatefulSetReadiness determins if a StatefulSet is ready, failed or
124+
// neither.
125+
func checkStatefulSetReadiness(oc client.Interface, obj runtime.Object) (bool, bool, error) {
126+
ss := obj.(*apps.StatefulSet)
127+
128+
ready := ss.Status.ObservedGeneration != nil &&
129+
*ss.Status.ObservedGeneration == ss.Generation &&
130+
ss.Status.ReadyReplicas == ss.Spec.Replicas
131+
failed := false
132+
133+
return ready, failed, nil
134+
}
135+
136+
// readinessCheckers maps GroupKinds to the appropriate function. Note that in
137+
// some cases more than one GK maps to the same function.
138+
var readinessCheckers = map[schema.GroupKind]func(client.Interface, runtime.Object) (bool, bool, error){
139+
buildapi.LegacyKind("Build"): checkBuildReadiness,
140+
buildapi.Kind("Build"): checkBuildReadiness,
141+
buildapi.LegacyKind("BuildConfig"): checkBuildConfigReadiness,
142+
buildapi.Kind("BuildConfig"): checkBuildConfigReadiness,
143+
apps.Kind("Deployment"): checkDeploymentReadiness,
144+
extensions.Kind("Deployment"): checkDeploymentReadiness,
145+
deployapi.LegacyKind("DeploymentConfig"): checkDeploymentConfigReadiness,
146+
deployapi.Kind("DeploymentConfig"): checkDeploymentConfigReadiness,
147+
batch.Kind("Job"): checkJobReadiness,
148+
apps.Kind("StatefulSet"): checkStatefulSetReadiness,
149+
}
150+
151+
// canCheckReadiness indicates whether a readiness check exists for a GK.
152+
func canCheckReadiness(ref kapi.ObjectReference) bool {
153+
_, found := readinessCheckers[ref.GroupVersionKind().GroupKind()]
154+
return found
155+
}
156+
157+
// checkReadiness runs the readiness check on a given object. TODO: remove
158+
// "oc client.Interface" and error once BuildConfigs can report on the status of
159+
// their latest build.
160+
func checkReadiness(oc client.Interface, ref kapi.ObjectReference, obj runtime.Object) (bool, bool, error) {
161+
return readinessCheckers[ref.GroupVersionKind().GroupKind()](oc, obj)
162+
}

0 commit comments

Comments
 (0)