1
16
17 package polymorphichelpers
18
19 import (
20 "fmt"
21
22 appsv1 "k8s.io/api/apps/v1"
23 extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
24 "k8s.io/apimachinery/pkg/runtime"
25 "k8s.io/apimachinery/pkg/runtime/schema"
26 deploymentutil "k8s.io/kubectl/pkg/util/deployment"
27 )
28
29
30 type StatusViewer interface {
31 Status(obj runtime.Unstructured, revision int64) (string, bool, error)
32 }
33
34
35 func StatusViewerFor(kind schema.GroupKind) (StatusViewer, error) {
36 switch kind {
37 case extensionsv1beta1.SchemeGroupVersion.WithKind("Deployment").GroupKind(),
38 appsv1.SchemeGroupVersion.WithKind("Deployment").GroupKind():
39 return &DeploymentStatusViewer{}, nil
40 case extensionsv1beta1.SchemeGroupVersion.WithKind("DaemonSet").GroupKind(),
41 appsv1.SchemeGroupVersion.WithKind("DaemonSet").GroupKind():
42 return &DaemonSetStatusViewer{}, nil
43 case appsv1.SchemeGroupVersion.WithKind("StatefulSet").GroupKind():
44 return &StatefulSetStatusViewer{}, nil
45 }
46 return nil, fmt.Errorf("no status viewer has been implemented for %v", kind)
47 }
48
49
50 type DeploymentStatusViewer struct{}
51
52
53 type DaemonSetStatusViewer struct{}
54
55
56 type StatefulSetStatusViewer struct{}
57
58
59 func (s *DeploymentStatusViewer) Status(obj runtime.Unstructured, revision int64) (string, bool, error) {
60 deployment := &appsv1.Deployment{}
61 err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), deployment)
62 if err != nil {
63 return "", false, fmt.Errorf("failed to convert %T to %T: %v", obj, deployment, err)
64 }
65
66 if revision > 0 {
67 deploymentRev, err := deploymentutil.Revision(deployment)
68 if err != nil {
69 return "", false, fmt.Errorf("cannot get the revision of deployment %q: %v", deployment.Name, err)
70 }
71 if revision != deploymentRev {
72 return "", false, fmt.Errorf("desired revision (%d) is different from the running revision (%d)", revision, deploymentRev)
73 }
74 }
75 if deployment.Generation <= deployment.Status.ObservedGeneration {
76 cond := deploymentutil.GetDeploymentCondition(deployment.Status, appsv1.DeploymentProgressing)
77 if cond != nil && cond.Reason == deploymentutil.TimedOutReason {
78 return "", false, fmt.Errorf("deployment %q exceeded its progress deadline", deployment.Name)
79 }
80 if deployment.Spec.Replicas != nil && deployment.Status.UpdatedReplicas < *deployment.Spec.Replicas {
81 return fmt.Sprintf("Waiting for deployment %q rollout to finish: %d out of %d new replicas have been updated...\n", deployment.Name, deployment.Status.UpdatedReplicas, *deployment.Spec.Replicas), false, nil
82 }
83 if deployment.Status.Replicas > deployment.Status.UpdatedReplicas {
84 return fmt.Sprintf("Waiting for deployment %q rollout to finish: %d old replicas are pending termination...\n", deployment.Name, deployment.Status.Replicas-deployment.Status.UpdatedReplicas), false, nil
85 }
86 if deployment.Status.AvailableReplicas < deployment.Status.UpdatedReplicas {
87 return fmt.Sprintf("Waiting for deployment %q rollout to finish: %d of %d updated replicas are available...\n", deployment.Name, deployment.Status.AvailableReplicas, deployment.Status.UpdatedReplicas), false, nil
88 }
89 return fmt.Sprintf("deployment %q successfully rolled out\n", deployment.Name), true, nil
90 }
91 return fmt.Sprintf("Waiting for deployment spec update to be observed...\n"), false, nil
92 }
93
94
95 func (s *DaemonSetStatusViewer) Status(obj runtime.Unstructured, revision int64) (string, bool, error) {
96
97
98 daemon := &appsv1.DaemonSet{}
99 err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), daemon)
100 if err != nil {
101 return "", false, fmt.Errorf("failed to convert %T to %T: %v", obj, daemon, err)
102 }
103
104 if daemon.Spec.UpdateStrategy.Type != appsv1.RollingUpdateDaemonSetStrategyType {
105 return "", true, fmt.Errorf("rollout status is only available for %s strategy type", appsv1.RollingUpdateStatefulSetStrategyType)
106 }
107 if daemon.Generation <= daemon.Status.ObservedGeneration {
108 if daemon.Status.UpdatedNumberScheduled < daemon.Status.DesiredNumberScheduled {
109 return fmt.Sprintf("Waiting for daemon set %q rollout to finish: %d out of %d new pods have been updated...\n", daemon.Name, daemon.Status.UpdatedNumberScheduled, daemon.Status.DesiredNumberScheduled), false, nil
110 }
111 if daemon.Status.NumberAvailable < daemon.Status.DesiredNumberScheduled {
112 return fmt.Sprintf("Waiting for daemon set %q rollout to finish: %d of %d updated pods are available...\n", daemon.Name, daemon.Status.NumberAvailable, daemon.Status.DesiredNumberScheduled), false, nil
113 }
114 return fmt.Sprintf("daemon set %q successfully rolled out\n", daemon.Name), true, nil
115 }
116 return fmt.Sprintf("Waiting for daemon set spec update to be observed...\n"), false, nil
117 }
118
119
120 func (s *StatefulSetStatusViewer) Status(obj runtime.Unstructured, revision int64) (string, bool, error) {
121 sts := &appsv1.StatefulSet{}
122 err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), sts)
123 if err != nil {
124 return "", false, fmt.Errorf("failed to convert %T to %T: %v", obj, sts, err)
125 }
126
127 if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType {
128 return "", true, fmt.Errorf("rollout status is only available for %s strategy type", appsv1.RollingUpdateStatefulSetStrategyType)
129 }
130 if sts.Status.ObservedGeneration == 0 || sts.Generation > sts.Status.ObservedGeneration {
131 return "Waiting for statefulset spec update to be observed...\n", false, nil
132 }
133 if sts.Spec.Replicas != nil && sts.Status.ReadyReplicas < *sts.Spec.Replicas {
134 return fmt.Sprintf("Waiting for %d pods to be ready...\n", *sts.Spec.Replicas-sts.Status.ReadyReplicas), false, nil
135 }
136 if sts.Spec.UpdateStrategy.Type == appsv1.RollingUpdateStatefulSetStrategyType && sts.Spec.UpdateStrategy.RollingUpdate != nil {
137 if sts.Spec.Replicas != nil && sts.Spec.UpdateStrategy.RollingUpdate.Partition != nil {
138 if sts.Status.UpdatedReplicas < (*sts.Spec.Replicas - *sts.Spec.UpdateStrategy.RollingUpdate.Partition) {
139 return fmt.Sprintf("Waiting for partitioned roll out to finish: %d out of %d new pods have been updated...\n",
140 sts.Status.UpdatedReplicas, *sts.Spec.Replicas-*sts.Spec.UpdateStrategy.RollingUpdate.Partition), false, nil
141 }
142 }
143 return fmt.Sprintf("partitioned roll out complete: %d new pods have been updated...\n",
144 sts.Status.UpdatedReplicas), true, nil
145 }
146 if sts.Status.UpdateRevision != sts.Status.CurrentRevision {
147 return fmt.Sprintf("waiting for statefulset rolling update to complete %d pods at revision %s...\n",
148 sts.Status.UpdatedReplicas, sts.Status.UpdateRevision), false, nil
149 }
150 return fmt.Sprintf("statefulset rolling update complete %d pods at revision %s...\n", sts.Status.CurrentReplicas, sts.Status.CurrentRevision), true, nil
151
152 }
153
View as plain text