...

Source file src/k8s.io/kubectl/pkg/polymorphichelpers/rollout_status_test.go

Documentation: k8s.io/kubectl/pkg/polymorphichelpers

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package polymorphichelpers
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  
    23  	apps "k8s.io/api/apps/v1"
    24  	api "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  )
    29  
    30  func TestDeploymentStatusViewerStatus(t *testing.T) {
    31  	tests := []struct {
    32  		name         string
    33  		generation   int64
    34  		specReplicas int32
    35  		status       apps.DeploymentStatus
    36  		msg          string
    37  		done         bool
    38  	}{
    39  		{
    40  			name:         "test1",
    41  			generation:   0,
    42  			specReplicas: 1,
    43  			status: apps.DeploymentStatus{
    44  				ObservedGeneration:  1,
    45  				Replicas:            1,
    46  				UpdatedReplicas:     0,
    47  				AvailableReplicas:   1,
    48  				UnavailableReplicas: 0,
    49  			},
    50  
    51  			msg:  "Waiting for deployment \"foo\" rollout to finish: 0 out of 1 new replicas have been updated...\n",
    52  			done: false,
    53  		},
    54  		{
    55  			name:         "test2",
    56  			generation:   1,
    57  			specReplicas: 1,
    58  			status: apps.DeploymentStatus{
    59  				ObservedGeneration:  1,
    60  				Replicas:            2,
    61  				UpdatedReplicas:     1,
    62  				AvailableReplicas:   2,
    63  				UnavailableReplicas: 0,
    64  			},
    65  
    66  			msg:  "Waiting for deployment \"foo\" rollout to finish: 1 old replicas are pending termination...\n",
    67  			done: false,
    68  		},
    69  		{
    70  			name:         "test3",
    71  			generation:   1,
    72  			specReplicas: 2,
    73  			status: apps.DeploymentStatus{
    74  				ObservedGeneration:  1,
    75  				Replicas:            2,
    76  				UpdatedReplicas:     2,
    77  				AvailableReplicas:   1,
    78  				UnavailableReplicas: 1,
    79  			},
    80  
    81  			msg:  "Waiting for deployment \"foo\" rollout to finish: 1 of 2 updated replicas are available...\n",
    82  			done: false,
    83  		},
    84  		{
    85  			name:         "test4",
    86  			generation:   1,
    87  			specReplicas: 2,
    88  			status: apps.DeploymentStatus{
    89  				ObservedGeneration:  1,
    90  				Replicas:            2,
    91  				UpdatedReplicas:     2,
    92  				AvailableReplicas:   2,
    93  				UnavailableReplicas: 0,
    94  			},
    95  
    96  			msg:  "deployment \"foo\" successfully rolled out\n",
    97  			done: true,
    98  		},
    99  		{
   100  			name:         "test5",
   101  			generation:   2,
   102  			specReplicas: 2,
   103  			status: apps.DeploymentStatus{
   104  				ObservedGeneration:  1,
   105  				Replicas:            2,
   106  				UpdatedReplicas:     2,
   107  				AvailableReplicas:   2,
   108  				UnavailableReplicas: 0,
   109  			},
   110  
   111  			msg:  "Waiting for deployment spec update to be observed...\n",
   112  			done: false,
   113  		},
   114  	}
   115  
   116  	for _, test := range tests {
   117  		t.Run(test.name, func(t *testing.T) {
   118  			d := &apps.Deployment{
   119  				ObjectMeta: metav1.ObjectMeta{
   120  					Namespace:  "bar",
   121  					Name:       "foo",
   122  					UID:        "8764ae47-9092-11e4-8393-42010af018ff",
   123  					Generation: test.generation,
   124  				},
   125  				Spec: apps.DeploymentSpec{
   126  					Replicas: &test.specReplicas,
   127  				},
   128  				Status: test.status,
   129  			}
   130  			unstructuredD := &unstructured.Unstructured{}
   131  			var err error
   132  			unstructuredD.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(d)
   133  			if err != nil {
   134  				t.Fatal(err)
   135  			}
   136  
   137  			dsv := &DeploymentStatusViewer{}
   138  			msg, done, err := dsv.Status(unstructuredD, 0)
   139  			if err != nil {
   140  				t.Fatalf("DeploymentStatusViewer.Status(): %v", err)
   141  			}
   142  			if done != test.done || msg != test.msg {
   143  				t.Errorf("DeploymentStatusViewer.Status() for deployment with generation %d, %d replicas specified, and status %+v returned %q, %t, want %q, %t",
   144  					test.generation,
   145  					test.specReplicas,
   146  					test.status,
   147  					msg,
   148  					done,
   149  					test.msg,
   150  					test.done,
   151  				)
   152  			}
   153  		})
   154  	}
   155  }
   156  
   157  func TestDaemonSetStatusViewerStatus(t *testing.T) {
   158  	tests := []struct {
   159  		name       string
   160  		generation int64
   161  		status     apps.DaemonSetStatus
   162  		msg        string
   163  		done       bool
   164  	}{
   165  		{
   166  			name:       "test1",
   167  			generation: 0,
   168  			status: apps.DaemonSetStatus{
   169  				ObservedGeneration:     1,
   170  				UpdatedNumberScheduled: 0,
   171  				DesiredNumberScheduled: 1,
   172  				NumberAvailable:        0,
   173  			},
   174  
   175  			msg:  "Waiting for daemon set \"foo\" rollout to finish: 0 out of 1 new pods have been updated...\n",
   176  			done: false,
   177  		},
   178  		{
   179  			name:       "test2",
   180  			generation: 1,
   181  			status: apps.DaemonSetStatus{
   182  				ObservedGeneration:     1,
   183  				UpdatedNumberScheduled: 2,
   184  				DesiredNumberScheduled: 2,
   185  				NumberAvailable:        1,
   186  			},
   187  
   188  			msg:  "Waiting for daemon set \"foo\" rollout to finish: 1 of 2 updated pods are available...\n",
   189  			done: false,
   190  		},
   191  		{
   192  			name:       "test3",
   193  			generation: 1,
   194  			status: apps.DaemonSetStatus{
   195  				ObservedGeneration:     1,
   196  				UpdatedNumberScheduled: 2,
   197  				DesiredNumberScheduled: 2,
   198  				NumberAvailable:        2,
   199  			},
   200  
   201  			msg:  "daemon set \"foo\" successfully rolled out\n",
   202  			done: true,
   203  		},
   204  		{
   205  			name:       "test4",
   206  			generation: 2,
   207  			status: apps.DaemonSetStatus{
   208  				ObservedGeneration:     1,
   209  				UpdatedNumberScheduled: 2,
   210  				DesiredNumberScheduled: 2,
   211  				NumberAvailable:        2,
   212  			},
   213  
   214  			msg:  "Waiting for daemon set spec update to be observed...\n",
   215  			done: false,
   216  		},
   217  	}
   218  
   219  	for _, test := range tests {
   220  		t.Run(test.name, func(t *testing.T) {
   221  			d := &apps.DaemonSet{
   222  				ObjectMeta: metav1.ObjectMeta{
   223  					Namespace:  "bar",
   224  					Name:       "foo",
   225  					UID:        "8764ae47-9092-11e4-8393-42010af018ff",
   226  					Generation: test.generation,
   227  				},
   228  				Spec: apps.DaemonSetSpec{
   229  					UpdateStrategy: apps.DaemonSetUpdateStrategy{
   230  						Type: apps.RollingUpdateDaemonSetStrategyType,
   231  					},
   232  				},
   233  				Status: test.status,
   234  			}
   235  
   236  			unstructuredD := &unstructured.Unstructured{}
   237  			var err error
   238  			unstructuredD.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(d)
   239  			if err != nil {
   240  				t.Fatal(err)
   241  			}
   242  
   243  			dsv := &DaemonSetStatusViewer{}
   244  			msg, done, err := dsv.Status(unstructuredD, 0)
   245  			if err != nil {
   246  				t.Fatalf("unexpected error: %v", err)
   247  			}
   248  			if done != test.done || msg != test.msg {
   249  				t.Errorf("daemon set with generation %d, %d pods specified, and status:\n%+v\nreturned:\n%q, %t\nwant:\n%q, %t",
   250  					test.generation,
   251  					d.Status.DesiredNumberScheduled,
   252  					test.status,
   253  					msg,
   254  					done,
   255  					test.msg,
   256  					test.done,
   257  				)
   258  			}
   259  		})
   260  	}
   261  }
   262  
   263  func TestStatefulSetStatusViewerStatus(t *testing.T) {
   264  	tests := []struct {
   265  		name       string
   266  		generation int64
   267  		strategy   apps.StatefulSetUpdateStrategy
   268  		status     apps.StatefulSetStatus
   269  		msg        string
   270  		done       bool
   271  		err        bool
   272  	}{
   273  		{
   274  			name:       "on delete returns an error",
   275  			generation: 1,
   276  			strategy:   apps.StatefulSetUpdateStrategy{Type: apps.OnDeleteStatefulSetStrategyType},
   277  			status: apps.StatefulSetStatus{
   278  				ObservedGeneration: 1,
   279  				Replicas:           0,
   280  				ReadyReplicas:      1,
   281  				CurrentReplicas:    0,
   282  				UpdatedReplicas:    0,
   283  			},
   284  
   285  			msg:  "",
   286  			done: true,
   287  			err:  true,
   288  		},
   289  		{
   290  			name:       "unobserved update is not complete",
   291  			generation: 2,
   292  			strategy:   apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
   293  			status: apps.StatefulSetStatus{
   294  				ObservedGeneration: 1,
   295  				Replicas:           3,
   296  				ReadyReplicas:      3,
   297  				CurrentReplicas:    3,
   298  				UpdatedReplicas:    0,
   299  			},
   300  
   301  			msg:  "Waiting for statefulset spec update to be observed...\n",
   302  			done: false,
   303  			err:  false,
   304  		},
   305  		{
   306  			name:       "if all pods are not ready the update is not complete",
   307  			generation: 1,
   308  			strategy:   apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
   309  			status: apps.StatefulSetStatus{
   310  				ObservedGeneration: 2,
   311  				Replicas:           3,
   312  				ReadyReplicas:      2,
   313  				CurrentReplicas:    3,
   314  				UpdatedReplicas:    0,
   315  			},
   316  
   317  			msg:  fmt.Sprintf("Waiting for %d pods to be ready...\n", 1),
   318  			done: false,
   319  			err:  false,
   320  		},
   321  		{
   322  			name:       "partition update completes when all replicas above the partition are updated",
   323  			generation: 1,
   324  			strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType,
   325  				RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
   326  					partition := int32(2)
   327  					return &apps.RollingUpdateStatefulSetStrategy{Partition: &partition}
   328  				}()},
   329  			status: apps.StatefulSetStatus{
   330  				ObservedGeneration: 2,
   331  				Replicas:           3,
   332  				ReadyReplicas:      3,
   333  				CurrentReplicas:    2,
   334  				UpdatedReplicas:    1,
   335  			},
   336  
   337  			msg:  fmt.Sprintf("partitioned roll out complete: %d new pods have been updated...\n", 1),
   338  			done: true,
   339  			err:  false,
   340  		},
   341  		{
   342  			name:       "partition update is in progress if all pods above the partition have not been updated",
   343  			generation: 1,
   344  			strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType,
   345  				RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
   346  					partition := int32(2)
   347  					return &apps.RollingUpdateStatefulSetStrategy{Partition: &partition}
   348  				}()},
   349  			status: apps.StatefulSetStatus{
   350  				ObservedGeneration: 2,
   351  				Replicas:           3,
   352  				ReadyReplicas:      3,
   353  				CurrentReplicas:    3,
   354  				UpdatedReplicas:    0,
   355  			},
   356  
   357  			msg:  fmt.Sprintf("Waiting for partitioned roll out to finish: %d out of %d new pods have been updated...\n", 0, 1),
   358  			done: true,
   359  			err:  false,
   360  		},
   361  		{
   362  			name:       "update completes when all replicas are current",
   363  			generation: 1,
   364  			strategy:   apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
   365  			status: apps.StatefulSetStatus{
   366  				ObservedGeneration: 2,
   367  				Replicas:           3,
   368  				ReadyReplicas:      3,
   369  				CurrentReplicas:    3,
   370  				UpdatedReplicas:    3,
   371  				CurrentRevision:    "foo",
   372  				UpdateRevision:     "foo",
   373  			},
   374  
   375  			msg:  fmt.Sprintf("statefulset rolling update complete %d pods at revision %s...\n", 3, "foo"),
   376  			done: true,
   377  			err:  false,
   378  		},
   379  	}
   380  
   381  	for _, test := range tests {
   382  		t.Run(test.name, func(t *testing.T) {
   383  			s := newStatefulSet(3)
   384  			s.Status = test.status
   385  			s.Spec.UpdateStrategy = test.strategy
   386  			s.Generation = test.generation
   387  
   388  			unstructuredS := &unstructured.Unstructured{}
   389  			var err error
   390  			unstructuredS.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(s)
   391  			if err != nil {
   392  				t.Fatal(err)
   393  			}
   394  
   395  			dsv := &StatefulSetStatusViewer{}
   396  			msg, done, err := dsv.Status(unstructuredS, 0)
   397  			if test.err && err == nil {
   398  				t.Fatalf("%s: expected error", test.name)
   399  			}
   400  			if !test.err && err != nil {
   401  				t.Fatalf("%s: %s", test.name, err)
   402  			}
   403  			if done && !test.done {
   404  				t.Errorf("%s: want done %v got %v", test.name, done, test.done)
   405  			}
   406  			if msg != test.msg {
   407  				t.Errorf("%s: want message %s got %s", test.name, test.msg, msg)
   408  			}
   409  		})
   410  	}
   411  }
   412  
   413  func TestDaemonSetStatusViewerStatusWithWrongUpdateStrategyType(t *testing.T) {
   414  	d := &apps.DaemonSet{
   415  		ObjectMeta: metav1.ObjectMeta{
   416  			Namespace: "bar",
   417  			Name:      "foo",
   418  			UID:       "8764ae47-9092-11e4-8393-42010af018ff",
   419  		},
   420  		Spec: apps.DaemonSetSpec{
   421  			UpdateStrategy: apps.DaemonSetUpdateStrategy{
   422  				Type: apps.OnDeleteDaemonSetStrategyType,
   423  			},
   424  		},
   425  	}
   426  
   427  	unstructuredD := &unstructured.Unstructured{}
   428  	var err error
   429  	unstructuredD.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(d)
   430  	if err != nil {
   431  		t.Fatal(err)
   432  	}
   433  
   434  	dsv := &DaemonSetStatusViewer{}
   435  	msg, done, err := dsv.Status(unstructuredD, 0)
   436  	errMsg := "rollout status is only available for RollingUpdate strategy type"
   437  	if err == nil || err.Error() != errMsg {
   438  		t.Errorf("Status for daemon sets with UpdateStrategy type different than RollingUpdate should return error. Instead got: msg: %s\ndone: %t\n err: %v", msg, done, err)
   439  	}
   440  }
   441  
   442  func newStatefulSet(replicas int32) *apps.StatefulSet {
   443  	return &apps.StatefulSet{
   444  		ObjectMeta: metav1.ObjectMeta{
   445  			Name:      "foo",
   446  			Namespace: metav1.NamespaceDefault,
   447  			Labels:    map[string]string{"a": "b"},
   448  		},
   449  		Spec: apps.StatefulSetSpec{
   450  			PodManagementPolicy: apps.OrderedReadyPodManagement,
   451  			Selector:            &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
   452  			Template: api.PodTemplateSpec{
   453  				ObjectMeta: metav1.ObjectMeta{
   454  					Labels: map[string]string{"a": "b"},
   455  				},
   456  				Spec: api.PodSpec{
   457  					Containers: []api.Container{
   458  						{
   459  							Name:            "test",
   460  							Image:           "test_image",
   461  							ImagePullPolicy: api.PullIfNotPresent,
   462  						},
   463  					},
   464  					RestartPolicy: api.RestartPolicyAlways,
   465  					DNSPolicy:     api.DNSClusterFirst,
   466  				},
   467  			},
   468  			Replicas:       &replicas,
   469  			UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
   470  		},
   471  		Status: apps.StatefulSetStatus{},
   472  	}
   473  }
   474  

View as plain text