github.com/ali-iotechsys/cli@v20.10.0+incompatible/cli/command/stack/kubernetes/watcher_test.go (about)

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