1/* 2Copyright 2016 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package polymorphichelpers 18 19import ( 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// StatusViewer provides an interface for resources that have rollout status. 30type StatusViewer interface { 31 Status(obj runtime.Unstructured, revision int64) (string, bool, error) 32} 33 34// StatusViewerFor returns a StatusViewer for the resource specified by kind. 35func 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// DeploymentStatusViewer implements the StatusViewer interface. 50type DeploymentStatusViewer struct{} 51 52// DaemonSetStatusViewer implements the StatusViewer interface. 53type DaemonSetStatusViewer struct{} 54 55// StatefulSetStatusViewer implements the StatusViewer interface. 56type StatefulSetStatusViewer struct{} 57 58// Status returns a message describing deployment status, and a bool value indicating if the status is considered done. 59func (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// Status returns a message describing daemon set status, and a bool value indicating if the status is considered done. 95func (s *DaemonSetStatusViewer) Status(obj runtime.Unstructured, revision int64) (string, bool, error) { 96 //ignoring revision as DaemonSets does not have history yet 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// Status returns a message describing statefulset status, and a bool value indicating if the status is considered done. 120func (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