github.com/AliyunContainerService/cli@v0.0.0-20181009023821-814ced4b30d0/cli/command/stack/kubernetes/watcher_test.go (about)

     1  package kubernetes
     2  
     3  import (
     4  	"testing"
     5  
     6  	apiv1beta1 "github.com/docker/cli/kubernetes/compose/v1beta1"
     7  	composelabels "github.com/docker/cli/kubernetes/labels"
     8  	"gotest.tools/assert"
     9  	apiv1 "k8s.io/api/core/v1"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/apimachinery/pkg/labels"
    12  	"k8s.io/apimachinery/pkg/runtime"
    13  	"k8s.io/apimachinery/pkg/runtime/schema"
    14  	"k8s.io/apimachinery/pkg/runtime/serializer"
    15  	"k8s.io/apimachinery/pkg/watch"
    16  	k8stesting "k8s.io/client-go/testing"
    17  )
    18  
    19  var podsResource = apiv1.SchemeGroupVersion.WithResource("pods")
    20  var podKind = apiv1.SchemeGroupVersion.WithKind("Pod")
    21  var stacksResource = apiv1beta1.SchemeGroupVersion.WithResource("stacks")
    22  var stackKind = apiv1beta1.SchemeGroupVersion.WithKind("Stack")
    23  
    24  type testPodAndStackRepository struct {
    25  	fake *k8stesting.Fake
    26  }
    27  
    28  func (r *testPodAndStackRepository) stackListWatchForNamespace(ns string) *testStackListWatch {
    29  	return &testStackListWatch{fake: r.fake, ns: ns}
    30  }
    31  func (r *testPodAndStackRepository) podListWatchForNamespace(ns string) *testPodListWatch {
    32  	return &testPodListWatch{fake: r.fake, ns: ns}
    33  }
    34  
    35  func newTestPodAndStackRepository(initialPods []apiv1.Pod, initialStacks []apiv1beta1.Stack, podWatchHandler, stackWatchHandler k8stesting.WatchReactionFunc) *testPodAndStackRepository {
    36  	var scheme = runtime.NewScheme()
    37  	var codecs = serializer.NewCodecFactory(scheme)
    38  	metav1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
    39  	apiv1.AddToScheme(scheme)
    40  	apiv1beta1.AddToScheme(scheme)
    41  
    42  	o := k8stesting.NewObjectTracker(scheme, codecs.UniversalDecoder())
    43  	for _, obj := range initialPods {
    44  		if err := o.Add(&obj); err != nil {
    45  			panic(err)
    46  		}
    47  	}
    48  	for _, obj := range initialStacks {
    49  		if err := o.Add(&obj); err != nil {
    50  			panic(err)
    51  		}
    52  	}
    53  	fakePtr := &k8stesting.Fake{}
    54  	fakePtr.AddReactor("*", "*", k8stesting.ObjectReaction(o))
    55  	if podWatchHandler != nil {
    56  		fakePtr.AddWatchReactor(podsResource.Resource, podWatchHandler)
    57  	}
    58  	if stackWatchHandler != nil {
    59  		fakePtr.AddWatchReactor(stacksResource.Resource, stackWatchHandler)
    60  	}
    61  	fakePtr.AddWatchReactor("*", k8stesting.DefaultWatchReactor(watch.NewFake(), nil))
    62  	return &testPodAndStackRepository{fake: fakePtr}
    63  }
    64  
    65  type testStackListWatch struct {
    66  	fake *k8stesting.Fake
    67  	ns   string
    68  }
    69  
    70  func (s *testStackListWatch) List(opts metav1.ListOptions) (*apiv1beta1.StackList, error) {
    71  	obj, err := s.fake.Invokes(k8stesting.NewListAction(stacksResource, stackKind, s.ns, opts), &apiv1beta1.StackList{})
    72  
    73  	if obj == nil {
    74  		return nil, err
    75  	}
    76  
    77  	label, _, _ := k8stesting.ExtractFromListOptions(opts)
    78  	if label == nil {
    79  		label = labels.Everything()
    80  	}
    81  	list := &apiv1beta1.StackList{}
    82  	for _, item := range obj.(*apiv1beta1.StackList).Items {
    83  		if label.Matches(labels.Set(item.Labels)) {
    84  			list.Items = append(list.Items, item)
    85  		}
    86  	}
    87  	return list, err
    88  }
    89  func (s *testStackListWatch) Watch(opts metav1.ListOptions) (watch.Interface, error) {
    90  	return s.fake.InvokesWatch(k8stesting.NewWatchAction(stacksResource, s.ns, opts))
    91  }
    92  
    93  type testPodListWatch struct {
    94  	fake *k8stesting.Fake
    95  	ns   string
    96  }
    97  
    98  func (p *testPodListWatch) List(opts metav1.ListOptions) (*apiv1.PodList, error) {
    99  	obj, err := p.fake.Invokes(k8stesting.NewListAction(podsResource, podKind, p.ns, opts), &apiv1.PodList{})
   100  
   101  	if obj == nil {
   102  		return nil, err
   103  	}
   104  
   105  	label, _, _ := k8stesting.ExtractFromListOptions(opts)
   106  	if label == nil {
   107  		label = labels.Everything()
   108  	}
   109  	list := &apiv1.PodList{}
   110  	for _, item := range obj.(*apiv1.PodList).Items {
   111  		if label.Matches(labels.Set(item.Labels)) {
   112  			list.Items = append(list.Items, item)
   113  		}
   114  	}
   115  	return list, err
   116  
   117  }
   118  func (p *testPodListWatch) Watch(opts metav1.ListOptions) (watch.Interface, error) {
   119  	return p.fake.InvokesWatch(k8stesting.NewWatchAction(podsResource, p.ns, opts))
   120  }
   121  
   122  func TestDeployWatchOk(t *testing.T) {
   123  	stack := apiv1beta1.Stack{
   124  		ObjectMeta: metav1.ObjectMeta{Name: "test-stack", Namespace: "test-ns"},
   125  	}
   126  
   127  	serviceNames := []string{"svc1", "svc2"}
   128  	testRepo := newTestPodAndStackRepository(nil, []apiv1beta1.Stack{stack}, func(action k8stesting.Action) (handled bool, ret watch.Interface, err error) {
   129  		res := watch.NewFake()
   130  		go func() {
   131  			pod1 := &apiv1.Pod{
   132  				ObjectMeta: metav1.ObjectMeta{
   133  					Name:      "test1",
   134  					Namespace: "test-ns",
   135  					Labels:    composelabels.ForService("test-stack", "svc1"),
   136  				},
   137  				Status: apiv1.PodStatus{
   138  					Phase: apiv1.PodRunning,
   139  					Conditions: []apiv1.PodCondition{
   140  						{
   141  							Type:   apiv1.PodReady,
   142  							Status: apiv1.ConditionTrue,
   143  						},
   144  					},
   145  				},
   146  			}
   147  			pod2 := &apiv1.Pod{
   148  				ObjectMeta: metav1.ObjectMeta{
   149  					Name:      "test2",
   150  					Namespace: "test-ns",
   151  					Labels:    composelabels.ForService("test-stack", "svc2"),
   152  				},
   153  				Status: apiv1.PodStatus{
   154  					Phase: apiv1.PodRunning,
   155  					Conditions: []apiv1.PodCondition{
   156  						{
   157  							Type:   apiv1.PodReady,
   158  							Status: apiv1.ConditionTrue,
   159  						},
   160  					},
   161  				},
   162  			}
   163  			res.Add(pod1)
   164  			res.Add(pod2)
   165  		}()
   166  
   167  		return true, res, nil
   168  	}, nil)
   169  
   170  	testee := &deployWatcher{
   171  		stacks: testRepo.stackListWatchForNamespace("test-ns"),
   172  		pods:   testRepo.podListWatchForNamespace("test-ns"),
   173  	}
   174  
   175  	statusUpdates := make(chan serviceStatus)
   176  	go func() {
   177  		for range statusUpdates {
   178  		}
   179  	}()
   180  	defer close(statusUpdates)
   181  	err := testee.Watch(stack.Name, serviceNames, statusUpdates)
   182  	assert.NilError(t, err)
   183  }
   184  
   185  func TestDeployReconcileFailure(t *testing.T) {
   186  	stack := apiv1beta1.Stack{
   187  		ObjectMeta: metav1.ObjectMeta{Name: "test-stack", Namespace: "test-ns"},
   188  	}
   189  
   190  	serviceNames := []string{"svc1", "svc2"}
   191  	testRepo := newTestPodAndStackRepository(nil, []apiv1beta1.Stack{stack}, nil, func(action k8stesting.Action) (handled bool, ret watch.Interface, err error) {
   192  		res := watch.NewFake()
   193  		go func() {
   194  			sfailed := stack
   195  			sfailed.Status = apiv1beta1.StackStatus{
   196  				Phase:   apiv1beta1.StackFailure,
   197  				Message: "test error",
   198  			}
   199  			res.Modify(&sfailed)
   200  		}()
   201  
   202  		return true, res, nil
   203  	})
   204  
   205  	testee := &deployWatcher{
   206  		stacks: testRepo.stackListWatchForNamespace("test-ns"),
   207  		pods:   testRepo.podListWatchForNamespace("test-ns"),
   208  	}
   209  
   210  	statusUpdates := make(chan serviceStatus)
   211  	go func() {
   212  		for range statusUpdates {
   213  		}
   214  	}()
   215  	defer close(statusUpdates)
   216  	err := testee.Watch(stack.Name, serviceNames, statusUpdates)
   217  	assert.ErrorContains(t, err, "Failure: test error")
   218  }