...

Source file src/github.com/linkerd/linkerd2/controller/api/destination/watcher/profile_watcher_test.go

Documentation: github.com/linkerd/linkerd2/controller/api/destination/watcher

     1  package watcher
     2  
     3  import (
     4  	"testing"
     5  
     6  	"k8s.io/client-go/tools/cache"
     7  
     8  	sp "github.com/linkerd/linkerd2/controller/gen/apis/serviceprofile/v1alpha2"
     9  	"github.com/linkerd/linkerd2/controller/k8s"
    10  	logging "github.com/sirupsen/logrus"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  )
    13  
    14  var testServiceProfile = sp.ServiceProfile{
    15  	ObjectMeta: metav1.ObjectMeta{
    16  		Name:      "foobar.ns.svc.cluster.local",
    17  		Namespace: "linkerd",
    18  	},
    19  	Spec: sp.ServiceProfileSpec{
    20  		Routes: []*sp.RouteSpec{
    21  			{
    22  				Condition: &sp.RequestMatch{
    23  					PathRegex: "/x/y/z",
    24  				},
    25  				ResponseClasses: []*sp.ResponseClass{
    26  					{
    27  						Condition: &sp.ResponseMatch{
    28  							Status: &sp.Range{
    29  								Min: 500,
    30  							},
    31  						},
    32  						IsFailure: true,
    33  					},
    34  				},
    35  			},
    36  		},
    37  	},
    38  }
    39  
    40  var testServiceProfileResource = `
    41  apiVersion: linkerd.io/v1alpha2
    42  kind: ServiceProfile
    43  metadata:
    44    name: foobar.ns.svc.cluster.local
    45    namespace: linkerd
    46  spec:
    47    routes:
    48    - condition:
    49        pathRegex: "/x/y/z"
    50      responseClasses:
    51      - condition:
    52          status:
    53            min: 500
    54        isFailure: true`
    55  
    56  func TestProfileWatcherUpdates(t *testing.T) {
    57  	for _, tt := range []struct {
    58  		name             string
    59  		k8sConfigs       []string
    60  		id               ProfileID
    61  		expectedProfiles []*sp.ServiceProfileSpec
    62  	}{
    63  		{
    64  			name:       "service profile",
    65  			k8sConfigs: []string{testServiceProfileResource},
    66  			id:         ProfileID{Name: testServiceProfile.Name, Namespace: testServiceProfile.Namespace},
    67  			expectedProfiles: []*sp.ServiceProfileSpec{
    68  				&testServiceProfile.Spec,
    69  			},
    70  		},
    71  		{
    72  			name:       "service without profile",
    73  			k8sConfigs: []string{},
    74  			id:         ProfileID{Name: "foobar.ns.svc.cluster.local", Namespace: "ns"},
    75  			expectedProfiles: []*sp.ServiceProfileSpec{
    76  				nil,
    77  			},
    78  		},
    79  	} {
    80  		tt := tt // pin
    81  		t.Run(tt.name, func(t *testing.T) {
    82  			k8sAPI, err := k8s.NewFakeAPI(tt.k8sConfigs...)
    83  			if err != nil {
    84  				t.Fatalf("NewFakeAPI returned an error: %s", err)
    85  			}
    86  
    87  			watcher, err := NewProfileWatcher(k8sAPI, logging.WithField("test", t.Name()))
    88  			if err != nil {
    89  				t.Fatalf("can't create profile watcher: %s", err)
    90  			}
    91  
    92  			k8sAPI.Sync(nil)
    93  
    94  			listener := NewBufferingProfileListener()
    95  
    96  			watcher.Subscribe(tt.id, listener)
    97  
    98  			actualProfiles := make([]*sp.ServiceProfileSpec, 0)
    99  
   100  			listener.mu.RLock()
   101  			defer listener.mu.RUnlock()
   102  			for _, profile := range listener.Profiles {
   103  				if profile == nil {
   104  					actualProfiles = append(actualProfiles, nil)
   105  				} else {
   106  					actualProfiles = append(actualProfiles, &profile.Spec)
   107  				}
   108  			}
   109  
   110  			testCompare(t, tt.expectedProfiles, actualProfiles)
   111  		})
   112  	}
   113  }
   114  
   115  func TestProfileWatcherDeletes(t *testing.T) {
   116  	for _, tt := range []struct {
   117  		name           string
   118  		k8sConfigs     []string
   119  		id             ProfileID
   120  		objectToDelete interface{}
   121  	}{
   122  		{
   123  			name:           "can delete service profiles",
   124  			k8sConfigs:     []string{testServiceProfileResource},
   125  			id:             ProfileID{Name: testServiceProfile.Name, Namespace: testServiceProfile.Namespace},
   126  			objectToDelete: &testServiceProfile,
   127  		},
   128  		{
   129  			name:           "can delete service profiles wrapped in a DeletedFinalStateUnknown",
   130  			k8sConfigs:     []string{testServiceProfileResource},
   131  			id:             ProfileID{Name: testServiceProfile.Name, Namespace: testServiceProfile.Namespace},
   132  			objectToDelete: cache.DeletedFinalStateUnknown{Obj: &testServiceProfile},
   133  		},
   134  	} {
   135  		tt := tt // pin
   136  		t.Run(tt.name, func(t *testing.T) {
   137  			k8sAPI, err := k8s.NewFakeAPI(tt.k8sConfigs...)
   138  			if err != nil {
   139  				t.Fatalf("NewFakeAPI returned an error: %s", err)
   140  			}
   141  
   142  			watcher, err := NewProfileWatcher(k8sAPI, logging.WithField("test", t.Name()))
   143  			if err != nil {
   144  				t.Fatalf("can't create profile watcher: %s", err)
   145  			}
   146  			k8sAPI.Sync(nil)
   147  
   148  			listener := NewDeletingProfileListener()
   149  
   150  			watcher.Subscribe(tt.id, listener)
   151  
   152  			watcher.deleteProfile(tt.objectToDelete)
   153  
   154  			if listener.NumDeletes != 1 {
   155  				t.Fatalf("Expected to get 1 deletes but got %v", listener.NumDeletes)
   156  			}
   157  		})
   158  	}
   159  }
   160  

View as plain text