...

Source file src/k8s.io/kube-aggregator/pkg/apiserver/handler_apis_test.go

Documentation: k8s.io/kube-aggregator/pkg/apiserver

     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 apiserver
    18  
    19  import (
    20  	"io"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"net/http/httputil"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/util/sets"
    31  	"k8s.io/client-go/tools/cache"
    32  
    33  	apiregistration "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
    34  	aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
    35  	listers "k8s.io/kube-aggregator/pkg/client/listers/apiregistration/v1"
    36  )
    37  
    38  func TestAPIs(t *testing.T) {
    39  	tests := []struct {
    40  		name        string
    41  		enabled     sets.String
    42  		apiservices []*apiregistration.APIService
    43  		expected    *metav1.APIGroupList
    44  	}{
    45  		{
    46  			name:        "empty",
    47  			enabled:     sets.NewString("v1", "v1beta1"),
    48  			apiservices: []*apiregistration.APIService{},
    49  			expected: &metav1.APIGroupList{
    50  				TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
    51  				Groups: []metav1.APIGroup{
    52  					discoveryGroup(sets.NewString("v1", "v1beta1")),
    53  				},
    54  			},
    55  		},
    56  		{
    57  			name:        "v1 only",
    58  			enabled:     sets.NewString("v1"),
    59  			apiservices: []*apiregistration.APIService{},
    60  			expected: &metav1.APIGroupList{
    61  				TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
    62  				Groups: []metav1.APIGroup{
    63  					discoveryGroup(sets.NewString("v1")),
    64  				},
    65  			},
    66  		},
    67  		{
    68  			name:    "simple add",
    69  			enabled: sets.NewString("v1", "v1beta1"),
    70  			apiservices: []*apiregistration.APIService{
    71  				{
    72  					ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
    73  					Spec: apiregistration.APIServiceSpec{
    74  						Service: &apiregistration.ServiceReference{
    75  							Namespace: "ns",
    76  							Name:      "api",
    77  						},
    78  						Group:                "foo",
    79  						Version:              "v1",
    80  						GroupPriorityMinimum: 11,
    81  					},
    82  					Status: apiregistration.APIServiceStatus{
    83  						Conditions: []apiregistration.APIServiceCondition{
    84  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
    85  						},
    86  					},
    87  				},
    88  				{
    89  					ObjectMeta: metav1.ObjectMeta{Name: "v1.bar"},
    90  					Spec: apiregistration.APIServiceSpec{
    91  						Service: &apiregistration.ServiceReference{
    92  							Namespace: "ns",
    93  							Name:      "api",
    94  						},
    95  						Group:                "bar",
    96  						Version:              "v1",
    97  						GroupPriorityMinimum: 10,
    98  					},
    99  					Status: apiregistration.APIServiceStatus{
   100  						Conditions: []apiregistration.APIServiceCondition{
   101  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   102  						},
   103  					},
   104  				},
   105  			},
   106  			expected: &metav1.APIGroupList{
   107  				TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
   108  				Groups: []metav1.APIGroup{
   109  					discoveryGroup(sets.NewString("v1", "v1beta1")),
   110  					{
   111  						Name: "foo",
   112  						Versions: []metav1.GroupVersionForDiscovery{
   113  							{
   114  								GroupVersion: "foo/v1",
   115  								Version:      "v1",
   116  							},
   117  						},
   118  						PreferredVersion: metav1.GroupVersionForDiscovery{
   119  							GroupVersion: "foo/v1",
   120  							Version:      "v1",
   121  						},
   122  					},
   123  					{
   124  						Name: "bar",
   125  						Versions: []metav1.GroupVersionForDiscovery{
   126  							{
   127  								GroupVersion: "bar/v1",
   128  								Version:      "v1",
   129  							},
   130  						},
   131  						PreferredVersion: metav1.GroupVersionForDiscovery{
   132  							GroupVersion: "bar/v1",
   133  							Version:      "v1",
   134  						},
   135  					},
   136  				},
   137  			},
   138  		},
   139  		{
   140  			name:    "sorting",
   141  			enabled: sets.NewString("v1", "v1beta1"),
   142  			apiservices: []*apiregistration.APIService{
   143  				{
   144  					ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
   145  					Spec: apiregistration.APIServiceSpec{
   146  						Service: &apiregistration.ServiceReference{
   147  							Namespace: "ns",
   148  							Name:      "api",
   149  						},
   150  						Group:                "foo",
   151  						Version:              "v1",
   152  						GroupPriorityMinimum: 20,
   153  						VersionPriority:      10,
   154  					},
   155  					Status: apiregistration.APIServiceStatus{
   156  						Conditions: []apiregistration.APIServiceCondition{
   157  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   158  						},
   159  					},
   160  				},
   161  				{
   162  					ObjectMeta: metav1.ObjectMeta{Name: "v2.bar"},
   163  					Spec: apiregistration.APIServiceSpec{
   164  						Service: &apiregistration.ServiceReference{
   165  							Namespace: "ns",
   166  							Name:      "api",
   167  						},
   168  						Group:                "bar",
   169  						Version:              "v2",
   170  						GroupPriorityMinimum: 11,
   171  					},
   172  					Status: apiregistration.APIServiceStatus{
   173  						Conditions: []apiregistration.APIServiceCondition{
   174  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   175  						},
   176  					},
   177  				},
   178  				{
   179  					ObjectMeta: metav1.ObjectMeta{Name: "v2.foo"},
   180  					Spec: apiregistration.APIServiceSpec{
   181  						Service: &apiregistration.ServiceReference{
   182  							Namespace: "ns",
   183  							Name:      "api",
   184  						},
   185  						Group:                "foo",
   186  						Version:              "v2",
   187  						GroupPriorityMinimum: 1,
   188  						VersionPriority:      15,
   189  					},
   190  					Status: apiregistration.APIServiceStatus{
   191  						Conditions: []apiregistration.APIServiceCondition{
   192  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   193  						},
   194  					},
   195  				},
   196  				{
   197  					ObjectMeta: metav1.ObjectMeta{Name: "v1.bar"},
   198  					Spec: apiregistration.APIServiceSpec{
   199  						Service: &apiregistration.ServiceReference{
   200  							Namespace: "ns",
   201  							Name:      "api",
   202  						},
   203  						Group:                "bar",
   204  						Version:              "v1",
   205  						GroupPriorityMinimum: 11,
   206  					},
   207  					Status: apiregistration.APIServiceStatus{
   208  						Conditions: []apiregistration.APIServiceCondition{
   209  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   210  						},
   211  					},
   212  				},
   213  			},
   214  			expected: &metav1.APIGroupList{
   215  				TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
   216  				Groups: []metav1.APIGroup{
   217  					discoveryGroup(sets.NewString("v1", "v1beta1")),
   218  					{
   219  						Name: "foo",
   220  						Versions: []metav1.GroupVersionForDiscovery{
   221  							{
   222  								GroupVersion: "foo/v2",
   223  								Version:      "v2",
   224  							},
   225  							{
   226  								GroupVersion: "foo/v1",
   227  								Version:      "v1",
   228  							},
   229  						},
   230  						PreferredVersion: metav1.GroupVersionForDiscovery{
   231  							GroupVersion: "foo/v2",
   232  							Version:      "v2",
   233  						},
   234  					},
   235  					{
   236  						Name: "bar",
   237  						Versions: []metav1.GroupVersionForDiscovery{
   238  							{
   239  								GroupVersion: "bar/v2",
   240  								Version:      "v2",
   241  							},
   242  							{
   243  								GroupVersion: "bar/v1",
   244  								Version:      "v1",
   245  							},
   246  						},
   247  						PreferredVersion: metav1.GroupVersionForDiscovery{
   248  							GroupVersion: "bar/v2",
   249  							Version:      "v2",
   250  						},
   251  					},
   252  				},
   253  			},
   254  		},
   255  		{
   256  			name:    "unavailable service",
   257  			enabled: sets.NewString("v1", "v1beta1"),
   258  			apiservices: []*apiregistration.APIService{
   259  				{
   260  					ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
   261  					Spec: apiregistration.APIServiceSpec{
   262  						Service: &apiregistration.ServiceReference{
   263  							Namespace: "ns",
   264  							Name:      "api",
   265  						},
   266  						Group:                "foo",
   267  						Version:              "v1",
   268  						GroupPriorityMinimum: 11,
   269  					},
   270  					Status: apiregistration.APIServiceStatus{
   271  						Conditions: []apiregistration.APIServiceCondition{
   272  							{Type: apiregistration.Available, Status: apiregistration.ConditionFalse},
   273  						},
   274  					},
   275  				},
   276  			},
   277  			expected: &metav1.APIGroupList{
   278  				TypeMeta: metav1.TypeMeta{Kind: "APIGroupList", APIVersion: "v1"},
   279  				Groups: []metav1.APIGroup{
   280  					discoveryGroup(sets.NewString("v1", "v1beta1")),
   281  					{
   282  						Name: "foo",
   283  						Versions: []metav1.GroupVersionForDiscovery{
   284  							{
   285  								GroupVersion: "foo/v1",
   286  								Version:      "v1",
   287  							},
   288  						},
   289  						PreferredVersion: metav1.GroupVersionForDiscovery{
   290  							GroupVersion: "foo/v1",
   291  							Version:      "v1",
   292  						},
   293  					},
   294  				},
   295  			},
   296  		},
   297  	}
   298  
   299  	for _, tc := range tests {
   300  		indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
   301  		handler := &apisHandler{
   302  			codecs:         aggregatorscheme.Codecs,
   303  			lister:         listers.NewAPIServiceLister(indexer),
   304  			discoveryGroup: discoveryGroup(tc.enabled),
   305  		}
   306  		for _, o := range tc.apiservices {
   307  			indexer.Add(o)
   308  		}
   309  
   310  		server := httptest.NewServer(handler)
   311  		defer server.Close()
   312  
   313  		resp, err := http.Get(server.URL + "/apis")
   314  		if err != nil {
   315  			t.Errorf("%s: %v", tc.name, err)
   316  			continue
   317  		}
   318  		bytes, err := io.ReadAll(resp.Body)
   319  		if err != nil {
   320  			t.Errorf("%s: %v", tc.name, err)
   321  			continue
   322  		}
   323  
   324  		actual := &metav1.APIGroupList{}
   325  		if err := runtime.DecodeInto(aggregatorscheme.Codecs.UniversalDecoder(), bytes, actual); err != nil {
   326  			t.Errorf("%s: %v", tc.name, err)
   327  			continue
   328  		}
   329  		if !apiequality.Semantic.DeepEqual(tc.expected, actual) {
   330  			t.Errorf("%s: %v", tc.name, cmp.Diff(tc.expected, actual))
   331  			continue
   332  		}
   333  	}
   334  }
   335  
   336  func TestAPIGroupMissing(t *testing.T) {
   337  	indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
   338  	handler := &apiGroupHandler{
   339  		codecs:    aggregatorscheme.Codecs,
   340  		lister:    listers.NewAPIServiceLister(indexer),
   341  		groupName: "groupName",
   342  		delegate: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   343  			w.WriteHeader(http.StatusForbidden)
   344  		}),
   345  	}
   346  
   347  	server := httptest.NewServer(handler)
   348  	defer server.Close()
   349  
   350  	// this call should delegate
   351  	resp, err := http.Get(server.URL + "/apis/groupName/foo")
   352  	if err != nil {
   353  		t.Fatal(err)
   354  	}
   355  	if resp.StatusCode != http.StatusForbidden {
   356  		t.Fatalf("expected %v, got %v", http.StatusForbidden, resp.StatusCode)
   357  	}
   358  
   359  	// groupName still has no api services for it (like it was deleted), it should delegate
   360  	resp, err = http.Get(server.URL + "/apis/groupName/")
   361  	if err != nil {
   362  		t.Fatal(err)
   363  	}
   364  	if resp.StatusCode != http.StatusForbidden {
   365  		t.Fatalf("expected %v, got %v", http.StatusForbidden, resp.StatusCode)
   366  	}
   367  
   368  	// missing group should delegate still has no api services for it (like it was deleted)
   369  	resp, err = http.Get(server.URL + "/apis/missing")
   370  	if err != nil {
   371  		t.Fatal(err)
   372  	}
   373  	if resp.StatusCode != http.StatusForbidden {
   374  		t.Fatalf("expected %v, got %v", http.StatusForbidden, resp.StatusCode)
   375  	}
   376  }
   377  
   378  func TestAPIGroup(t *testing.T) {
   379  	tests := []struct {
   380  		name        string
   381  		group       string
   382  		apiservices []*apiregistration.APIService
   383  		expected    *metav1.APIGroup
   384  	}{
   385  		{
   386  			name:  "sorting",
   387  			group: "foo",
   388  			apiservices: []*apiregistration.APIService{
   389  				{
   390  					ObjectMeta: metav1.ObjectMeta{Name: "v1.foo"},
   391  					Spec: apiregistration.APIServiceSpec{
   392  						Service: &apiregistration.ServiceReference{
   393  							Namespace: "ns",
   394  							Name:      "api",
   395  						},
   396  						Group:                "foo",
   397  						Version:              "v1",
   398  						GroupPriorityMinimum: 20,
   399  						VersionPriority:      10,
   400  					},
   401  					Status: apiregistration.APIServiceStatus{
   402  						Conditions: []apiregistration.APIServiceCondition{
   403  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   404  						},
   405  					},
   406  				},
   407  				{
   408  					ObjectMeta: metav1.ObjectMeta{Name: "v2.bar"},
   409  					Spec: apiregistration.APIServiceSpec{
   410  						Service: &apiregistration.ServiceReference{
   411  							Namespace: "ns",
   412  							Name:      "api",
   413  						},
   414  						Group:                "bar",
   415  						Version:              "v2",
   416  						GroupPriorityMinimum: 11,
   417  					},
   418  					Status: apiregistration.APIServiceStatus{
   419  						Conditions: []apiregistration.APIServiceCondition{
   420  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   421  						},
   422  					},
   423  				},
   424  				{
   425  					ObjectMeta: metav1.ObjectMeta{Name: "v2.foo"},
   426  					Spec: apiregistration.APIServiceSpec{
   427  						Service: &apiregistration.ServiceReference{
   428  							Namespace: "ns",
   429  							Name:      "api",
   430  						},
   431  						Group:                "foo",
   432  						Version:              "v2",
   433  						GroupPriorityMinimum: 1,
   434  						VersionPriority:      15,
   435  					},
   436  					Status: apiregistration.APIServiceStatus{
   437  						Conditions: []apiregistration.APIServiceCondition{
   438  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   439  						},
   440  					},
   441  				},
   442  				{
   443  					ObjectMeta: metav1.ObjectMeta{Name: "v1.bar"},
   444  					Spec: apiregistration.APIServiceSpec{
   445  						Service: &apiregistration.ServiceReference{
   446  							Namespace: "ns",
   447  							Name:      "api",
   448  						},
   449  						Group:                "bar",
   450  						Version:              "v1",
   451  						GroupPriorityMinimum: 11,
   452  					},
   453  					Status: apiregistration.APIServiceStatus{
   454  						Conditions: []apiregistration.APIServiceCondition{
   455  							{Type: apiregistration.Available, Status: apiregistration.ConditionTrue},
   456  						},
   457  					},
   458  				},
   459  			},
   460  			expected: &metav1.APIGroup{
   461  				TypeMeta: metav1.TypeMeta{Kind: "APIGroup", APIVersion: "v1"},
   462  				Name:     "foo",
   463  				Versions: []metav1.GroupVersionForDiscovery{
   464  					{
   465  						GroupVersion: "foo/v2",
   466  						Version:      "v2",
   467  					},
   468  					{
   469  						GroupVersion: "foo/v1",
   470  						Version:      "v1",
   471  					},
   472  				},
   473  				PreferredVersion: metav1.GroupVersionForDiscovery{
   474  					GroupVersion: "foo/v2",
   475  					Version:      "v2",
   476  				},
   477  			},
   478  		},
   479  	}
   480  
   481  	for _, tc := range tests {
   482  		indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
   483  		handler := &apiGroupHandler{
   484  			codecs:    aggregatorscheme.Codecs,
   485  			lister:    listers.NewAPIServiceLister(indexer),
   486  			groupName: "foo",
   487  		}
   488  		for _, o := range tc.apiservices {
   489  			indexer.Add(o)
   490  		}
   491  
   492  		server := httptest.NewServer(handler)
   493  		defer server.Close()
   494  
   495  		resp, err := http.Get(server.URL + "/apis/" + tc.group)
   496  		if err != nil {
   497  			t.Errorf("%s: %v", tc.name, err)
   498  			continue
   499  		}
   500  		if resp.StatusCode != http.StatusOK {
   501  			response, _ := httputil.DumpResponse(resp, true)
   502  			t.Errorf("%s: %v", tc.name, string(response))
   503  			continue
   504  		}
   505  		bytes, err := io.ReadAll(resp.Body)
   506  		if err != nil {
   507  			t.Errorf("%s: %v", tc.name, err)
   508  			continue
   509  		}
   510  
   511  		actual := &metav1.APIGroup{}
   512  		if err := runtime.DecodeInto(aggregatorscheme.Codecs.UniversalDecoder(), bytes, actual); err != nil {
   513  			t.Errorf("%s: %v", tc.name, err)
   514  			continue
   515  		}
   516  		if !apiequality.Semantic.DeepEqual(tc.expected, actual) {
   517  			t.Errorf("%s: %v", tc.name, cmp.Diff(tc.expected, actual))
   518  			continue
   519  		}
   520  	}
   521  }
   522  

View as plain text