2018-01-09 18:57:14 +00:00
/ *
Copyright 2016 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package kubectl
import (
"fmt"
2018-07-18 14:47:22 +00:00
appsv1 "k8s.io/api/apps/v1"
2018-01-09 18:57:14 +00:00
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
2018-11-26 18:23:56 +00:00
"k8s.io/apimachinery/pkg/runtime"
2018-01-09 18:57:14 +00:00
"k8s.io/apimachinery/pkg/runtime/schema"
2018-11-26 18:23:56 +00:00
"k8s.io/kubernetes/pkg/kubectl/scheme"
deploymentutil "k8s.io/kubernetes/pkg/kubectl/util/deployment"
2018-01-09 18:57:14 +00:00
)
// StatusViewer provides an interface for resources that have rollout status.
type StatusViewer interface {
2018-11-26 18:23:56 +00:00
Status ( obj runtime . Unstructured , revision int64 ) ( string , bool , error )
2018-01-09 18:57:14 +00:00
}
// StatusViewerFor returns a StatusViewer for the resource specified by kind.
2018-11-26 18:23:56 +00:00
func StatusViewerFor ( kind schema . GroupKind ) ( StatusViewer , error ) {
2018-01-09 18:57:14 +00:00
switch kind {
2018-11-26 18:23:56 +00:00
case extensionsv1beta1 . SchemeGroupVersion . WithKind ( "Deployment" ) . GroupKind ( ) ,
appsv1 . SchemeGroupVersion . WithKind ( "Deployment" ) . GroupKind ( ) :
return & DeploymentStatusViewer { } , nil
case extensionsv1beta1 . SchemeGroupVersion . WithKind ( "DaemonSet" ) . GroupKind ( ) ,
appsv1 . SchemeGroupVersion . WithKind ( "DaemonSet" ) . GroupKind ( ) :
return & DaemonSetStatusViewer { } , nil
case appsv1 . SchemeGroupVersion . WithKind ( "StatefulSet" ) . GroupKind ( ) :
return & StatefulSetStatusViewer { } , nil
2018-01-09 18:57:14 +00:00
}
return nil , fmt . Errorf ( "no status viewer has been implemented for %v" , kind )
}
// DeploymentStatusViewer implements the StatusViewer interface.
2018-11-26 18:23:56 +00:00
type DeploymentStatusViewer struct { }
2018-01-09 18:57:14 +00:00
// DaemonSetStatusViewer implements the StatusViewer interface.
2018-11-26 18:23:56 +00:00
type DaemonSetStatusViewer struct { }
2018-01-09 18:57:14 +00:00
// StatefulSetStatusViewer implements the StatusViewer interface.
2018-11-26 18:23:56 +00:00
type StatefulSetStatusViewer struct { }
2018-01-09 18:57:14 +00:00
// Status returns a message describing deployment status, and a bool value indicating if the status is considered done.
2018-11-26 18:23:56 +00:00
func ( s * DeploymentStatusViewer ) Status ( obj runtime . Unstructured , revision int64 ) ( string , bool , error ) {
deployment := & appsv1 . Deployment { }
err := scheme . Scheme . Convert ( obj , deployment , nil )
2018-01-09 18:57:14 +00:00
if err != nil {
2018-11-26 18:23:56 +00:00
return "" , false , fmt . Errorf ( "failed to convert %T to %T: %v" , obj , deployment , err )
2018-01-09 18:57:14 +00:00
}
2018-11-26 18:23:56 +00:00
2018-01-09 18:57:14 +00:00
if revision > 0 {
2018-11-26 18:23:56 +00:00
deploymentRev , err := deploymentutil . Revision ( deployment )
2018-01-09 18:57:14 +00:00
if err != nil {
return "" , false , fmt . Errorf ( "cannot get the revision of deployment %q: %v" , deployment . Name , err )
}
if revision != deploymentRev {
return "" , false , fmt . Errorf ( "desired revision (%d) is different from the running revision (%d)" , revision , deploymentRev )
}
}
if deployment . Generation <= deployment . Status . ObservedGeneration {
2018-11-26 18:23:56 +00:00
cond := deploymentutil . GetDeploymentCondition ( deployment . Status , appsv1 . DeploymentProgressing )
if cond != nil && cond . Reason == deploymentutil . TimedOutReason {
return "" , false , fmt . Errorf ( "deployment %q exceeded its progress deadline" , deployment . Name )
2018-01-09 18:57:14 +00:00
}
if deployment . Spec . Replicas != nil && deployment . Status . UpdatedReplicas < * deployment . Spec . Replicas {
2018-11-26 18:23:56 +00:00
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
2018-01-09 18:57:14 +00:00
}
if deployment . Status . Replicas > deployment . Status . UpdatedReplicas {
2018-11-26 18:23:56 +00:00
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
2018-01-09 18:57:14 +00:00
}
if deployment . Status . AvailableReplicas < deployment . Status . UpdatedReplicas {
2018-11-26 18:23:56 +00:00
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
2018-01-09 18:57:14 +00:00
}
2018-11-26 18:23:56 +00:00
return fmt . Sprintf ( "deployment %q successfully rolled out\n" , deployment . Name ) , true , nil
2018-01-09 18:57:14 +00:00
}
return fmt . Sprintf ( "Waiting for deployment spec update to be observed...\n" ) , false , nil
}
// Status returns a message describing daemon set status, and a bool value indicating if the status is considered done.
2018-11-26 18:23:56 +00:00
func ( s * DaemonSetStatusViewer ) Status ( obj runtime . Unstructured , revision int64 ) ( string , bool , error ) {
2018-01-09 18:57:14 +00:00
//ignoring revision as DaemonSets does not have history yet
2018-11-26 18:23:56 +00:00
daemon := & appsv1 . DaemonSet { }
err := scheme . Scheme . Convert ( obj , daemon , nil )
2018-01-09 18:57:14 +00:00
if err != nil {
2018-11-26 18:23:56 +00:00
return "" , false , fmt . Errorf ( "failed to convert %T to %T: %v" , obj , daemon , err )
2018-01-09 18:57:14 +00:00
}
2018-11-26 18:23:56 +00:00
2018-07-18 14:47:22 +00:00
if daemon . Spec . UpdateStrategy . Type != appsv1 . RollingUpdateDaemonSetStrategyType {
2018-11-26 18:23:56 +00:00
return "" , true , fmt . Errorf ( "rollout status is only available for %s strategy type" , appsv1 . RollingUpdateStatefulSetStrategyType )
2018-01-09 18:57:14 +00:00
}
if daemon . Generation <= daemon . Status . ObservedGeneration {
if daemon . Status . UpdatedNumberScheduled < daemon . Status . DesiredNumberScheduled {
2018-11-26 18:23:56 +00:00
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
2018-01-09 18:57:14 +00:00
}
if daemon . Status . NumberAvailable < daemon . Status . DesiredNumberScheduled {
2018-11-26 18:23:56 +00:00
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
2018-01-09 18:57:14 +00:00
}
2018-11-26 18:23:56 +00:00
return fmt . Sprintf ( "daemon set %q successfully rolled out\n" , daemon . Name ) , true , nil
2018-01-09 18:57:14 +00:00
}
return fmt . Sprintf ( "Waiting for daemon set spec update to be observed...\n" ) , false , nil
}
// Status returns a message describing statefulset status, and a bool value indicating if the status is considered done.
2018-11-26 18:23:56 +00:00
func ( s * StatefulSetStatusViewer ) Status ( obj runtime . Unstructured , revision int64 ) ( string , bool , error ) {
sts := & appsv1 . StatefulSet { }
err := scheme . Scheme . Convert ( obj , sts , nil )
2018-01-09 18:57:14 +00:00
if err != nil {
2018-11-26 18:23:56 +00:00
return "" , false , fmt . Errorf ( "failed to convert %T to %T: %v" , obj , sts , err )
2018-01-09 18:57:14 +00:00
}
2018-11-26 18:23:56 +00:00
if sts . Spec . UpdateStrategy . Type != appsv1 . RollingUpdateStatefulSetStrategyType {
return "" , true , fmt . Errorf ( "rollout status is only available for %s strategy type" , appsv1 . RollingUpdateStatefulSetStrategyType )
2018-01-09 18:57:14 +00:00
}
2018-07-18 14:47:22 +00:00
if sts . Status . ObservedGeneration == 0 || sts . Generation > sts . Status . ObservedGeneration {
2018-01-09 18:57:14 +00:00
return "Waiting for statefulset spec update to be observed...\n" , false , nil
}
if sts . Spec . Replicas != nil && sts . Status . ReadyReplicas < * sts . Spec . Replicas {
return fmt . Sprintf ( "Waiting for %d pods to be ready...\n" , * sts . Spec . Replicas - sts . Status . ReadyReplicas ) , false , nil
}
2018-11-26 18:23:56 +00:00
if sts . Spec . UpdateStrategy . Type == appsv1 . RollingUpdateStatefulSetStrategyType && sts . Spec . UpdateStrategy . RollingUpdate != nil {
2018-01-09 18:57:14 +00:00
if sts . Spec . Replicas != nil && sts . Spec . UpdateStrategy . RollingUpdate . Partition != nil {
if sts . Status . UpdatedReplicas < ( * sts . Spec . Replicas - * sts . Spec . UpdateStrategy . RollingUpdate . Partition ) {
return fmt . Sprintf ( "Waiting for partitioned roll out to finish: %d out of %d new pods have been updated...\n" ,
2018-11-26 18:23:56 +00:00
sts . Status . UpdatedReplicas , * sts . Spec . Replicas - * sts . Spec . UpdateStrategy . RollingUpdate . Partition ) , false , nil
2018-01-09 18:57:14 +00:00
}
}
return fmt . Sprintf ( "partitioned roll out complete: %d new pods have been updated...\n" ,
sts . Status . UpdatedReplicas ) , true , nil
}
if sts . Status . UpdateRevision != sts . Status . CurrentRevision {
return fmt . Sprintf ( "waiting for statefulset rolling update to complete %d pods at revision %s...\n" ,
sts . Status . UpdatedReplicas , sts . Status . UpdateRevision ) , false , nil
}
return fmt . Sprintf ( "statefulset rolling update complete %d pods at revision %s...\n" , sts . Status . CurrentReplicas , sts . Status . CurrentRevision ) , true , nil
}