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 }