...

Source file src/k8s.io/kubernetes/pkg/serviceaccount/claims_test.go

Documentation: k8s.io/kubernetes/pkg/serviceaccount

     1  /*
     2  Copyright 2018 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 serviceaccount
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"testing"
    24  	"time"
    25  
    26  	"gopkg.in/square/go-jose.v2/jwt"
    27  
    28  	v1 "k8s.io/api/core/v1"
    29  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/runtime/schema"
    32  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    33  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    34  	"k8s.io/kubernetes/pkg/apis/core"
    35  	"k8s.io/kubernetes/pkg/features"
    36  )
    37  
    38  func init() {
    39  	now = func() time.Time {
    40  		// epoch time: 1514764800
    41  		return time.Date(2018, time.January, 1, 0, 0, 0, 0, time.UTC)
    42  	}
    43  
    44  	newUUID = func() string {
    45  		// always return a fixed/static UUID for testing
    46  		return "fixed"
    47  	}
    48  }
    49  
    50  func TestClaims(t *testing.T) {
    51  	sa := core.ServiceAccount{
    52  		ObjectMeta: metav1.ObjectMeta{
    53  			Namespace: "myns",
    54  			Name:      "mysvcacct",
    55  			UID:       "mysvcacct-uid",
    56  		},
    57  	}
    58  	pod := &core.Pod{
    59  		ObjectMeta: metav1.ObjectMeta{
    60  			Namespace: "myns",
    61  			Name:      "mypod",
    62  			UID:       "mypod-uid",
    63  		},
    64  	}
    65  	sec := &core.Secret{
    66  		ObjectMeta: metav1.ObjectMeta{
    67  			Namespace: "myns",
    68  			Name:      "mysecret",
    69  			UID:       "mysecret-uid",
    70  		},
    71  	}
    72  	node := &core.Node{
    73  		ObjectMeta: metav1.ObjectMeta{
    74  			Name: "mynode",
    75  			UID:  "mynode-uid",
    76  		},
    77  	}
    78  	cs := []struct {
    79  		// input
    80  		sa        core.ServiceAccount
    81  		pod       *core.Pod
    82  		sec       *core.Secret
    83  		node      *core.Node
    84  		exp       int64
    85  		warnafter int64
    86  		aud       []string
    87  		err       string
    88  		// desired
    89  		sc *jwt.Claims
    90  		pc *privateClaims
    91  
    92  		featureJTI, featurePodNodeInfo, featureNodeBinding bool
    93  	}{
    94  		{
    95  			// pod and secret
    96  			sa:  sa,
    97  			pod: pod,
    98  			sec: sec,
    99  			// really fast
   100  			exp: 0,
   101  			// nil audience
   102  			aud: nil,
   103  			err: "internal error, token can only be bound to one object type",
   104  		},
   105  		{
   106  			// pod
   107  			sa:  sa,
   108  			pod: pod,
   109  			// empty audience
   110  			aud: []string{},
   111  			exp: 100,
   112  
   113  			sc: &jwt.Claims{
   114  				Subject:   "system:serviceaccount:myns:mysvcacct",
   115  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   116  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   117  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800+100, 0)),
   118  			},
   119  			pc: &privateClaims{
   120  				Kubernetes: kubernetes{
   121  					Namespace: "myns",
   122  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   123  					Pod:       &ref{Name: "mypod", UID: "mypod-uid"},
   124  				},
   125  			},
   126  		},
   127  		{
   128  			// secret
   129  			sa:  sa,
   130  			sec: sec,
   131  			exp: 100,
   132  			// single member audience
   133  			aud: []string{"1"},
   134  
   135  			sc: &jwt.Claims{
   136  				Subject:   "system:serviceaccount:myns:mysvcacct",
   137  				Audience:  []string{"1"},
   138  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   139  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   140  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800+100, 0)),
   141  			},
   142  			pc: &privateClaims{
   143  				Kubernetes: kubernetes{
   144  					Namespace: "myns",
   145  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   146  					Secret:    &ref{Name: "mysecret", UID: "mysecret-uid"},
   147  				},
   148  			},
   149  		},
   150  		{
   151  			// no obj binding
   152  			sa:  sa,
   153  			exp: 100,
   154  			// multimember audience
   155  			aud: []string{"1", "2"},
   156  
   157  			sc: &jwt.Claims{
   158  				Subject:   "system:serviceaccount:myns:mysvcacct",
   159  				Audience:  []string{"1", "2"},
   160  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   161  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   162  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800+100, 0)),
   163  			},
   164  			pc: &privateClaims{
   165  				Kubernetes: kubernetes{
   166  					Namespace: "myns",
   167  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   168  				},
   169  			},
   170  		},
   171  		{
   172  			// warn after provided
   173  			sa:        sa,
   174  			pod:       pod,
   175  			exp:       60 * 60 * 24,
   176  			warnafter: 60 * 60,
   177  			// nil audience
   178  			aud: nil,
   179  
   180  			sc: &jwt.Claims{
   181  				Subject:   "system:serviceaccount:myns:mysvcacct",
   182  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   183  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   184  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800+60*60*24, 0)),
   185  			},
   186  			pc: &privateClaims{
   187  				Kubernetes: kubernetes{
   188  					Namespace: "myns",
   189  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   190  					Pod:       &ref{Name: "mypod", UID: "mypod-uid"},
   191  					WarnAfter: jwt.NewNumericDate(time.Unix(1514764800+60*60, 0)),
   192  				},
   193  			},
   194  		},
   195  		{
   196  			// node with feature gate disabled
   197  			sa:   sa,
   198  			node: node,
   199  			// really fast
   200  			exp: 0,
   201  			// nil audience
   202  			aud: nil,
   203  			err: "token bound to Node object requested, but \"ServiceAccountTokenNodeBinding\" feature gate is disabled",
   204  		},
   205  		{
   206  			// node & pod with feature gate disabled
   207  			sa:   sa,
   208  			node: node,
   209  			pod:  pod,
   210  			// really fast
   211  			exp: 0,
   212  			// nil audience
   213  			aud: nil,
   214  
   215  			sc: &jwt.Claims{
   216  				Subject:   "system:serviceaccount:myns:mysvcacct",
   217  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   218  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   219  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800, 0)),
   220  			},
   221  			pc: &privateClaims{
   222  				Kubernetes: kubernetes{
   223  					Namespace: "myns",
   224  					Pod:       &ref{Name: "mypod", UID: "mypod-uid"},
   225  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   226  				},
   227  			},
   228  		},
   229  		{
   230  			// node alone
   231  			sa:   sa,
   232  			node: node,
   233  			// enable node binding feature
   234  			featureNodeBinding: true,
   235  			// really fast
   236  			exp: 0,
   237  			// nil audience
   238  			aud: nil,
   239  
   240  			sc: &jwt.Claims{
   241  				Subject:   "system:serviceaccount:myns:mysvcacct",
   242  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   243  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   244  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800, 0)),
   245  			},
   246  			pc: &privateClaims{
   247  				Kubernetes: kubernetes{
   248  					Namespace: "myns",
   249  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   250  					Node:      &ref{Name: "mynode", UID: "mynode-uid"},
   251  				},
   252  			},
   253  		},
   254  		{
   255  			// node and pod
   256  			sa:   sa,
   257  			pod:  pod,
   258  			node: node,
   259  			// enable embedding pod node info feature
   260  			featurePodNodeInfo: true,
   261  			// really fast
   262  			exp: 0,
   263  			// nil audience
   264  			aud: nil,
   265  
   266  			sc: &jwt.Claims{
   267  				Subject:   "system:serviceaccount:myns:mysvcacct",
   268  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   269  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   270  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800, 0)),
   271  			},
   272  			pc: &privateClaims{
   273  				Kubernetes: kubernetes{
   274  					Namespace: "myns",
   275  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   276  					Pod:       &ref{Name: "mypod", UID: "mypod-uid"},
   277  					Node:      &ref{Name: "mynode", UID: "mynode-uid"},
   278  				},
   279  			},
   280  		},
   281  		{
   282  			// node and secret should error
   283  			sa:   sa,
   284  			sec:  sec,
   285  			node: node,
   286  			// enable embedding node info feature
   287  			featureNodeBinding: true,
   288  			// really fast
   289  			exp: 0,
   290  			// nil audience
   291  			aud: nil,
   292  			err: "internal error, token can only be bound to one object type",
   293  		},
   294  		{
   295  			// ensure JTI is set
   296  			sa: sa,
   297  			// enable setting JTI feature
   298  			featureJTI: true,
   299  			// really fast
   300  			exp: 0,
   301  			// nil audience
   302  			aud: nil,
   303  
   304  			sc: &jwt.Claims{
   305  				Subject:   "system:serviceaccount:myns:mysvcacct",
   306  				IssuedAt:  jwt.NewNumericDate(time.Unix(1514764800, 0)),
   307  				NotBefore: jwt.NewNumericDate(time.Unix(1514764800, 0)),
   308  				Expiry:    jwt.NewNumericDate(time.Unix(1514764800, 0)),
   309  				ID:        "fixed",
   310  			},
   311  			pc: &privateClaims{
   312  				Kubernetes: kubernetes{
   313  					Namespace: "myns",
   314  					Svcacct:   ref{Name: "mysvcacct", UID: "mysvcacct-uid"},
   315  				},
   316  			},
   317  		},
   318  		{
   319  			// ensure it fails if node binding gate is disabled
   320  			sa:                 sa,
   321  			node:               node,
   322  			featureNodeBinding: false,
   323  			// really fast
   324  			exp: 0,
   325  			// nil audience
   326  			aud: nil,
   327  
   328  			err: "token bound to Node object requested, but \"ServiceAccountTokenNodeBinding\" feature gate is disabled",
   329  		},
   330  	}
   331  	for i, c := range cs {
   332  		t.Run(fmt.Sprintf("case %d", i), func(t *testing.T) {
   333  			// comparing json spews has the benefit over
   334  			// reflect.DeepEqual that we are also asserting that
   335  			// claims structs are json serializable
   336  			spew := func(obj interface{}) string {
   337  				b, err := json.Marshal(obj)
   338  				if err != nil {
   339  					t.Fatalf("err, couldn't marshal claims: %v", err)
   340  				}
   341  				return string(b)
   342  			}
   343  
   344  			// set feature flags for the duration of the test case
   345  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenJTI, c.featureJTI)()
   346  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBinding, c.featureNodeBinding)()
   347  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenPodNodeInfo, c.featurePodNodeInfo)()
   348  
   349  			sc, pc, err := Claims(c.sa, c.pod, c.sec, c.node, c.exp, c.warnafter, c.aud)
   350  			if err != nil && err.Error() != c.err {
   351  				t.Errorf("expected error %q but got: %v", c.err, err)
   352  			}
   353  			if err == nil && c.err != "" {
   354  				t.Errorf("expected an error but got none")
   355  			}
   356  			if spew(sc) != spew(c.sc) {
   357  				t.Errorf("standard claims differed\n\tsaw:\t%s\n\twant:\t%s", spew(sc), spew(c.sc))
   358  			}
   359  			if spew(pc) != spew(c.pc) {
   360  				t.Errorf("private claims differed\n\tsaw: %s\n\twant: %s", spew(pc), spew(c.pc))
   361  			}
   362  		})
   363  	}
   364  }
   365  
   366  type deletionTestCase struct {
   367  	name      string
   368  	time      *metav1.Time
   369  	expectErr bool
   370  }
   371  
   372  type claimTestCase struct {
   373  	name      string
   374  	getter    ServiceAccountTokenGetter
   375  	private   *privateClaims
   376  	expiry    jwt.NumericDate
   377  	notBefore jwt.NumericDate
   378  	expectErr string
   379  
   380  	featureNodeBindingValidation bool
   381  }
   382  
   383  func TestValidatePrivateClaims(t *testing.T) {
   384  	var (
   385  		nowUnix = int64(1514764800)
   386  
   387  		serviceAccount = &v1.ServiceAccount{ObjectMeta: metav1.ObjectMeta{Name: "saname", Namespace: "ns", UID: "sauid"}}
   388  		secret         = &v1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "secretname", Namespace: "ns", UID: "secretuid"}}
   389  		pod            = &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "podname", Namespace: "ns", UID: "poduid"}}
   390  		node           = &v1.Node{ObjectMeta: metav1.ObjectMeta{Name: "nodename", UID: "nodeuid"}}
   391  	)
   392  
   393  	deletionTestCases := []deletionTestCase{
   394  		{
   395  			name: "valid",
   396  			time: nil,
   397  		},
   398  		{
   399  			name: "deleted now",
   400  			time: &metav1.Time{Time: time.Unix(nowUnix, 0)},
   401  		},
   402  		{
   403  			name: "deleted near past",
   404  			time: &metav1.Time{Time: time.Unix(nowUnix-1, 0)},
   405  		},
   406  		{
   407  			name: "deleted near future",
   408  			time: &metav1.Time{Time: time.Unix(nowUnix+1, 0)},
   409  		},
   410  		{
   411  			name: "deleted now-leeway",
   412  			time: &metav1.Time{Time: time.Unix(nowUnix-60, 0)},
   413  		},
   414  		{
   415  			name:      "deleted now-leeway-1",
   416  			time:      &metav1.Time{Time: time.Unix(nowUnix-61, 0)},
   417  			expectErr: true,
   418  		},
   419  	}
   420  
   421  	testcases := []claimTestCase{
   422  		{
   423  			name:      "good",
   424  			getter:    fakeGetter{serviceAccount, nil, nil, nil},
   425  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Namespace: "ns"}},
   426  			expectErr: "",
   427  		},
   428  		{
   429  			name:      "expired",
   430  			getter:    fakeGetter{serviceAccount, nil, nil, nil},
   431  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Namespace: "ns"}},
   432  			expiry:    *jwt.NewNumericDate(now().Add(-1_000 * time.Hour)),
   433  			expectErr: "service account token has expired",
   434  		},
   435  		{
   436  			name:      "not yet valid",
   437  			getter:    fakeGetter{serviceAccount, nil, nil, nil},
   438  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Namespace: "ns"}},
   439  			notBefore: *jwt.NewNumericDate(now().Add(1_000 * time.Hour)),
   440  			expectErr: "service account token is not valid yet",
   441  		},
   442  		{
   443  			name:      "missing serviceaccount",
   444  			getter:    fakeGetter{nil, nil, nil, nil},
   445  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Namespace: "ns"}},
   446  			expectErr: `serviceaccounts "saname" not found`,
   447  		},
   448  		{
   449  			name:      "missing secret",
   450  			getter:    fakeGetter{serviceAccount, nil, nil, nil},
   451  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Secret: &ref{Name: "secretname", UID: "secretuid"}, Namespace: "ns"}},
   452  			expectErr: "service account token has been invalidated",
   453  		},
   454  		{
   455  			name:      "missing pod",
   456  			getter:    fakeGetter{serviceAccount, nil, nil, nil},
   457  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Pod: &ref{Name: "podname", UID: "poduid"}, Namespace: "ns"}},
   458  			expectErr: "service account token has been invalidated",
   459  		},
   460  		{
   461  			name:                         "missing node",
   462  			getter:                       fakeGetter{serviceAccount, nil, nil, nil},
   463  			private:                      &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Node: &ref{Name: "nodename", UID: "nodeuid"}, Namespace: "ns"}},
   464  			expectErr:                    "service account token has been invalidated",
   465  			featureNodeBindingValidation: true,
   466  		},
   467  		{
   468  			name:      "different uid serviceaccount",
   469  			getter:    fakeGetter{serviceAccount, nil, nil, nil},
   470  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauidold"}, Namespace: "ns"}},
   471  			expectErr: "service account UID (sauid) does not match claim (sauidold)",
   472  		},
   473  		{
   474  			name:      "different uid secret",
   475  			getter:    fakeGetter{serviceAccount, secret, nil, nil},
   476  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Secret: &ref{Name: "secretname", UID: "secretuidold"}, Namespace: "ns"}},
   477  			expectErr: "secret UID (secretuid) does not match service account secret ref claim (secretuidold)",
   478  		},
   479  		{
   480  			name:      "different uid pod",
   481  			getter:    fakeGetter{serviceAccount, nil, pod, nil},
   482  			private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Pod: &ref{Name: "podname", UID: "poduidold"}, Namespace: "ns"}},
   483  			expectErr: "pod UID (poduid) does not match service account pod ref claim (poduidold)",
   484  		},
   485  	}
   486  
   487  	for _, deletionTestCase := range deletionTestCases {
   488  		var (
   489  			deletedServiceAccount = serviceAccount.DeepCopy()
   490  			deletedPod            = pod.DeepCopy()
   491  			deletedSecret         = secret.DeepCopy()
   492  			deletedNode           = node.DeepCopy()
   493  		)
   494  		deletedServiceAccount.DeletionTimestamp = deletionTestCase.time
   495  		deletedPod.DeletionTimestamp = deletionTestCase.time
   496  		deletedSecret.DeletionTimestamp = deletionTestCase.time
   497  		deletedNode.DeletionTimestamp = deletionTestCase.time
   498  
   499  		var saDeletedErr, deletedErr string
   500  		if deletionTestCase.expectErr {
   501  			saDeletedErr = "service account ns/saname has been deleted"
   502  			deletedErr = "service account token has been invalidated"
   503  		}
   504  
   505  		testcases = append(testcases,
   506  			claimTestCase{
   507  				name:      deletionTestCase.name + " serviceaccount",
   508  				getter:    fakeGetter{deletedServiceAccount, nil, nil, nil},
   509  				private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Namespace: "ns"}},
   510  				expectErr: saDeletedErr,
   511  			},
   512  			claimTestCase{
   513  				name:      deletionTestCase.name + " secret",
   514  				getter:    fakeGetter{serviceAccount, deletedSecret, nil, nil},
   515  				private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Secret: &ref{Name: "secretname", UID: "secretuid"}, Namespace: "ns"}},
   516  				expectErr: deletedErr,
   517  			},
   518  			claimTestCase{
   519  				name:      deletionTestCase.name + " pod",
   520  				getter:    fakeGetter{serviceAccount, nil, deletedPod, nil},
   521  				private:   &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Pod: &ref{Name: "podname", UID: "poduid"}, Namespace: "ns"}},
   522  				expectErr: deletedErr,
   523  			},
   524  			claimTestCase{
   525  				name:                         deletionTestCase.name + " node",
   526  				getter:                       fakeGetter{serviceAccount, nil, nil, deletedNode},
   527  				private:                      &privateClaims{Kubernetes: kubernetes{Svcacct: ref{Name: "saname", UID: "sauid"}, Node: &ref{Name: "nodename", UID: "nodeuid"}, Namespace: "ns"}},
   528  				expectErr:                    deletedErr,
   529  				featureNodeBindingValidation: true,
   530  			},
   531  		)
   532  	}
   533  
   534  	for _, tc := range testcases {
   535  		t.Run(tc.name, func(t *testing.T) {
   536  			v := &validator{getter: tc.getter}
   537  			expiry := jwt.NumericDate(nowUnix)
   538  			if tc.expiry != 0 {
   539  				expiry = tc.expiry
   540  			}
   541  
   542  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServiceAccountTokenNodeBindingValidation, tc.featureNodeBindingValidation)()
   543  
   544  			_, err := v.Validate(context.Background(), "", &jwt.Claims{Expiry: &expiry, NotBefore: &tc.notBefore}, tc.private)
   545  			if len(tc.expectErr) > 0 {
   546  				if errStr := errString(err); tc.expectErr != errStr {
   547  					t.Fatalf("expected error %q but got %q", tc.expectErr, errStr)
   548  				}
   549  			} else if err != nil {
   550  				t.Fatalf("unexpected error: %v", err)
   551  			}
   552  		})
   553  	}
   554  }
   555  
   556  func errString(err error) string {
   557  	if err == nil {
   558  		return ""
   559  	}
   560  
   561  	return err.Error()
   562  }
   563  
   564  type fakeGetter struct {
   565  	serviceAccount *v1.ServiceAccount
   566  	secret         *v1.Secret
   567  	pod            *v1.Pod
   568  	node           *v1.Node
   569  }
   570  
   571  func (f fakeGetter) GetServiceAccount(namespace, name string) (*v1.ServiceAccount, error) {
   572  	if f.serviceAccount == nil {
   573  		return nil, apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "serviceaccounts"}, name)
   574  	}
   575  	return f.serviceAccount, nil
   576  }
   577  func (f fakeGetter) GetPod(namespace, name string) (*v1.Pod, error) {
   578  	if f.pod == nil {
   579  		return nil, apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "pods"}, name)
   580  	}
   581  	return f.pod, nil
   582  }
   583  func (f fakeGetter) GetSecret(namespace, name string) (*v1.Secret, error) {
   584  	if f.secret == nil {
   585  		return nil, apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "secrets"}, name)
   586  	}
   587  	return f.secret, nil
   588  }
   589  func (f fakeGetter) GetNode(name string) (*v1.Node, error) {
   590  	if f.node == nil {
   591  		return nil, apierrors.NewNotFound(schema.GroupResource{Group: "", Resource: "nodes"}, name)
   592  	}
   593  	return f.node, nil
   594  }
   595  

View as plain text