...

Source file src/k8s.io/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer_test.go

Documentation: k8s.io/kubernetes/plugin/pkg/auth/authorizer/node

     1  /*
     2  Copyright 2017 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 node
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"math/rand"
    23  	"os"
    24  	"runtime"
    25  	"runtime/pprof"
    26  	"sync/atomic"
    27  	"testing"
    28  	"time"
    29  
    30  	corev1 "k8s.io/api/core/v1"
    31  	resourcev1alpha2 "k8s.io/api/resource/v1alpha2"
    32  	storagev1 "k8s.io/api/storage/v1"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apiserver/pkg/authentication/user"
    35  	"k8s.io/apiserver/pkg/authorization/authorizer"
    36  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    37  	"k8s.io/component-base/featuregate"
    38  	"k8s.io/kubernetes/pkg/auth/nodeidentifier"
    39  	"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy"
    40  )
    41  
    42  func TestAuthorizer(t *testing.T) {
    43  	g := NewGraph()
    44  
    45  	opts := &sampleDataOpts{
    46  		nodes:                              2,
    47  		namespaces:                         2,
    48  		podsPerNode:                        2,
    49  		attachmentsPerNode:                 1,
    50  		sharedConfigMapsPerPod:             0,
    51  		uniqueConfigMapsPerPod:             1,
    52  		sharedSecretsPerPod:                1,
    53  		uniqueSecretsPerPod:                1,
    54  		sharedPVCsPerPod:                   0,
    55  		uniquePVCsPerPod:                   1,
    56  		uniqueResourceClaimsPerPod:         1,
    57  		uniqueResourceClaimTemplatesPerPod: 1,
    58  		uniqueResourceClaimTemplatesWithClaimPerPod: 1,
    59  		nodeResourceCapacitiesPerNode:               2,
    60  	}
    61  	nodes, pods, pvs, attachments, slices := generate(opts)
    62  	populate(g, nodes, pods, pvs, attachments, slices)
    63  
    64  	identifier := nodeidentifier.NewDefaultNodeIdentifier()
    65  	authz := NewAuthorizer(g, identifier, bootstrappolicy.NodeRules())
    66  
    67  	node0 := &user.DefaultInfo{Name: "system:node:node0", Groups: []string{"system:nodes"}}
    68  
    69  	tests := []struct {
    70  		name     string
    71  		attrs    authorizer.AttributesRecord
    72  		expect   authorizer.Decision
    73  		features featuregate.FeatureGate
    74  	}{
    75  		{
    76  			name:   "allowed configmap",
    77  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"},
    78  			expect: authorizer.DecisionAllow,
    79  		},
    80  		{
    81  			name:   "allowed secret via pod",
    82  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: "ns0"},
    83  			expect: authorizer.DecisionAllow,
    84  		},
    85  		{
    86  			name:   "list allowed secret via pod",
    87  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: "ns0"},
    88  			expect: authorizer.DecisionAllow,
    89  		},
    90  		{
    91  			name:   "watch allowed secret via pod",
    92  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: "ns0"},
    93  			expect: authorizer.DecisionAllow,
    94  		},
    95  		{
    96  			name:   "disallowed list many secrets",
    97  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "secrets", Name: "", Namespace: "ns0"},
    98  			expect: authorizer.DecisionNoOpinion,
    99  		},
   100  		{
   101  			name:   "disallowed watch many secrets",
   102  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "secrets", Name: "", Namespace: "ns0"},
   103  			expect: authorizer.DecisionNoOpinion,
   104  		},
   105  		{
   106  			name:   "disallowed list secrets from all namespaces with name",
   107  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: ""},
   108  			expect: authorizer.DecisionNoOpinion,
   109  		},
   110  		{
   111  			name:   "allowed shared secret via pod",
   112  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-shared", Namespace: "ns0"},
   113  			expect: authorizer.DecisionAllow,
   114  		},
   115  		{
   116  			name:   "allowed shared secret via pvc",
   117  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node0-ns0", Namespace: "ns0"},
   118  			expect: authorizer.DecisionAllow,
   119  		},
   120  		{
   121  			name:   "allowed pvc",
   122  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node0", Namespace: "ns0"},
   123  			expect: authorizer.DecisionAllow,
   124  		},
   125  		{
   126  			name:   "allowed resource claim",
   127  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "claim0-pod0-node0-ns0", Namespace: "ns0"},
   128  			expect: authorizer.DecisionAllow,
   129  		},
   130  		{
   131  			name:   "allowed resource claim with template",
   132  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "generated-claim-pod0-node0-ns0-0", Namespace: "ns0"},
   133  			expect: authorizer.DecisionAllow,
   134  		},
   135  		{
   136  			name:   "allowed pv",
   137  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node0-ns0", Namespace: ""},
   138  			expect: authorizer.DecisionAllow,
   139  		},
   140  		{
   141  			name:   "disallowed configmap",
   142  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"},
   143  			expect: authorizer.DecisionNoOpinion,
   144  		},
   145  		{
   146  			name:   "disallowed secret via pod",
   147  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node1", Namespace: "ns0"},
   148  			expect: authorizer.DecisionNoOpinion,
   149  		},
   150  		{
   151  			name:   "disallowed shared secret via pvc",
   152  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node1-ns0", Namespace: "ns0"},
   153  			expect: authorizer.DecisionNoOpinion,
   154  		},
   155  		{
   156  			name:   "disallowed pvc",
   157  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node1", Namespace: "ns0"},
   158  			expect: authorizer.DecisionNoOpinion,
   159  		},
   160  		{
   161  			name:   "disallowed resource claim, other node",
   162  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "claim0-pod0-node1-ns0", Namespace: "ns0"},
   163  			expect: authorizer.DecisionNoOpinion,
   164  		},
   165  		{
   166  			name:   "disallowed resource claim with template",
   167  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceclaims", APIGroup: "resource.k8s.io", Name: "pod0-node1-claimtemplate0", Namespace: "ns0"},
   168  			expect: authorizer.DecisionNoOpinion,
   169  		},
   170  		{
   171  			name:   "disallowed pv",
   172  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node1-ns0", Namespace: ""},
   173  			expect: authorizer.DecisionNoOpinion,
   174  		},
   175  		{
   176  			name:   "allowed attachment",
   177  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "volumeattachments", APIGroup: "storage.k8s.io", Name: "attachment0-node0"},
   178  			expect: authorizer.DecisionAllow,
   179  		},
   180  		{
   181  			name:   "allowed svcacct token create",
   182  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
   183  			expect: authorizer.DecisionAllow,
   184  		},
   185  		{
   186  			name:   "disallowed svcacct token create - serviceaccount not attached to node",
   187  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node1", Namespace: "ns0"},
   188  			expect: authorizer.DecisionNoOpinion,
   189  		},
   190  		{
   191  			name:   "disallowed svcacct token create - no subresource",
   192  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "serviceaccounts", Name: "svcacct0-node0", Namespace: "ns0"},
   193  			expect: authorizer.DecisionNoOpinion,
   194  		},
   195  		{
   196  			name:   "disallowed svcacct token create - non create",
   197  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "serviceaccounts", Subresource: "token", Name: "svcacct0-node0", Namespace: "ns0"},
   198  			expect: authorizer.DecisionNoOpinion,
   199  		},
   200  		{
   201  			name:   "disallowed get lease in namespace other than kube-node-lease - feature enabled",
   202  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"},
   203  			expect: authorizer.DecisionNoOpinion,
   204  		},
   205  		{
   206  			name:   "disallowed create lease in namespace other than kube-node-lease - feature enabled",
   207  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"},
   208  			expect: authorizer.DecisionNoOpinion,
   209  		},
   210  		{
   211  			name:   "disallowed update lease in namespace other than kube-node-lease - feature enabled",
   212  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"},
   213  			expect: authorizer.DecisionNoOpinion,
   214  		},
   215  		{
   216  			name:   "disallowed patch lease in namespace other than kube-node-lease - feature enabled",
   217  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"},
   218  			expect: authorizer.DecisionNoOpinion,
   219  		},
   220  		{
   221  			name:   "disallowed delete lease in namespace other than kube-node-lease - feature enabled",
   222  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: "foo"},
   223  			expect: authorizer.DecisionNoOpinion,
   224  		},
   225  		{
   226  			name:   "disallowed get another node's lease - feature enabled",
   227  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease},
   228  			expect: authorizer.DecisionNoOpinion,
   229  		},
   230  		{
   231  			name:   "disallowed update another node's lease - feature enabled",
   232  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease},
   233  			expect: authorizer.DecisionNoOpinion,
   234  		},
   235  		{
   236  			name:   "disallowed patch another node's lease - feature enabled",
   237  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease},
   238  			expect: authorizer.DecisionNoOpinion,
   239  		},
   240  		{
   241  			name:   "disallowed delete another node's lease - feature enabled",
   242  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node1", Namespace: corev1.NamespaceNodeLease},
   243  			expect: authorizer.DecisionNoOpinion,
   244  		},
   245  		{
   246  			name:   "disallowed list node leases - feature enabled",
   247  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "leases", APIGroup: "coordination.k8s.io", Namespace: corev1.NamespaceNodeLease},
   248  			expect: authorizer.DecisionNoOpinion,
   249  		},
   250  		{
   251  			name:   "disallowed watch node leases - feature enabled",
   252  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "leases", APIGroup: "coordination.k8s.io", Namespace: corev1.NamespaceNodeLease},
   253  			expect: authorizer.DecisionNoOpinion,
   254  		},
   255  		{
   256  			name:   "allowed get node lease - feature enabled",
   257  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease},
   258  			expect: authorizer.DecisionAllow,
   259  		},
   260  		{
   261  			name:   "allowed create node lease - feature enabled",
   262  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease},
   263  			expect: authorizer.DecisionAllow,
   264  		},
   265  		{
   266  			name:   "allowed update node lease - feature enabled",
   267  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease},
   268  			expect: authorizer.DecisionAllow,
   269  		},
   270  		{
   271  			name:   "allowed patch node lease - feature enabled",
   272  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease},
   273  			expect: authorizer.DecisionAllow,
   274  		},
   275  		{
   276  			name:   "allowed delete node lease - feature enabled",
   277  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "leases", APIGroup: "coordination.k8s.io", Name: "node0", Namespace: corev1.NamespaceNodeLease},
   278  			expect: authorizer.DecisionAllow,
   279  		},
   280  		// CSINode
   281  		{
   282  			name:   "disallowed CSINode with subresource - feature enabled",
   283  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "csinodes", Subresource: "csiDrivers", APIGroup: "storage.k8s.io", Name: "node0"},
   284  			expect: authorizer.DecisionNoOpinion,
   285  		},
   286  		{
   287  			name:   "disallowed get another node's CSINode",
   288  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node1"},
   289  			expect: authorizer.DecisionNoOpinion,
   290  		},
   291  		{
   292  			name:   "disallowed update another node's CSINode",
   293  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node1"},
   294  			expect: authorizer.DecisionNoOpinion,
   295  		},
   296  		{
   297  			name:   "disallowed patch another node's CSINode",
   298  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node1"},
   299  			expect: authorizer.DecisionNoOpinion,
   300  		},
   301  		{
   302  			name:   "disallowed delete another node's CSINode",
   303  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node1"},
   304  			expect: authorizer.DecisionNoOpinion,
   305  		},
   306  		{
   307  			name:   "disallowed list CSINodes",
   308  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "csinodes", APIGroup: "storage.k8s.io"},
   309  			expect: authorizer.DecisionNoOpinion,
   310  		},
   311  		{
   312  			name:   "disallowed watch CSINodes",
   313  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "csinodes", APIGroup: "storage.k8s.io"},
   314  			expect: authorizer.DecisionNoOpinion,
   315  		},
   316  		{
   317  			name:   "allowed get CSINode",
   318  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node0"},
   319  			expect: authorizer.DecisionAllow,
   320  		},
   321  		{
   322  			name:   "allowed create CSINode",
   323  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node0"},
   324  			expect: authorizer.DecisionAllow,
   325  		},
   326  		{
   327  			name:   "allowed update CSINode",
   328  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node0"},
   329  			expect: authorizer.DecisionAllow,
   330  		},
   331  		{
   332  			name:   "allowed patch CSINode",
   333  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node0"},
   334  			expect: authorizer.DecisionAllow,
   335  		},
   336  		{
   337  			name:   "allowed delete CSINode",
   338  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "csinodes", APIGroup: "storage.k8s.io", Name: "node0"},
   339  			expect: authorizer.DecisionAllow,
   340  		},
   341  		// ResourceSlice
   342  		{
   343  			name:   "disallowed ResourceSlice with subresource",
   344  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceslices", Subresource: "status", APIGroup: "resource.k8s.io", Name: "slice0-node0"},
   345  			expect: authorizer.DecisionNoOpinion,
   346  		},
   347  		{
   348  			name:   "disallowed get another node's ResourceSlice",
   349  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node1"},
   350  			expect: authorizer.DecisionNoOpinion,
   351  		},
   352  		{
   353  			name:   "disallowed update another node's ResourceSlice",
   354  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node1"},
   355  			expect: authorizer.DecisionNoOpinion,
   356  		},
   357  		{
   358  			name:   "disallowed patch another node's ResourceSlice",
   359  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node1"},
   360  			expect: authorizer.DecisionNoOpinion,
   361  		},
   362  		{
   363  			name:   "disallowed delete another node's ResourceSlice",
   364  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node1"},
   365  			expect: authorizer.DecisionNoOpinion,
   366  		},
   367  		{
   368  			name:   "allowed list ResourceSlices",
   369  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "list", Resource: "resourceslices", APIGroup: "resource.k8s.io"},
   370  			expect: authorizer.DecisionAllow,
   371  		},
   372  		{
   373  			name:   "allowed watch ResourceSlices",
   374  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "watch", Resource: "resourceslices", APIGroup: "resource.k8s.io"},
   375  			expect: authorizer.DecisionAllow,
   376  		},
   377  		{
   378  			name:   "allowed get ResourceSlice",
   379  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node0"},
   380  			expect: authorizer.DecisionAllow,
   381  		},
   382  		{
   383  			name:   "allowed create ResourceSlice",
   384  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "create", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node0"},
   385  			expect: authorizer.DecisionAllow,
   386  		},
   387  		{
   388  			name:   "allowed update ResourceSlice",
   389  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "update", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node0"},
   390  			expect: authorizer.DecisionAllow,
   391  		},
   392  		{
   393  			name:   "allowed patch ResourceSlice",
   394  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "patch", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node0"},
   395  			expect: authorizer.DecisionAllow,
   396  		},
   397  		{
   398  			name:   "allowed delete ResourceSlice",
   399  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "delete", Resource: "resourceslices", APIGroup: "resource.k8s.io", Name: "slice0-node0"},
   400  			expect: authorizer.DecisionAllow,
   401  		},
   402  	}
   403  
   404  	for _, tc := range tests {
   405  		t.Run(tc.name, func(t *testing.T) {
   406  			if tc.features == nil {
   407  				authz.features = utilfeature.DefaultFeatureGate
   408  			} else {
   409  				authz.features = tc.features
   410  			}
   411  			decision, _, _ := authz.Authorize(context.Background(), tc.attrs)
   412  			if decision != tc.expect {
   413  				t.Errorf("expected %v, got %v", tc.expect, decision)
   414  			}
   415  		})
   416  	}
   417  }
   418  
   419  func TestAuthorizerSharedResources(t *testing.T) {
   420  	g := NewGraph()
   421  	g.destinationEdgeThreshold = 1
   422  	identifier := nodeidentifier.NewDefaultNodeIdentifier()
   423  	authz := NewAuthorizer(g, identifier, bootstrappolicy.NodeRules())
   424  
   425  	node1 := &user.DefaultInfo{Name: "system:node:node1", Groups: []string{"system:nodes"}}
   426  	node2 := &user.DefaultInfo{Name: "system:node:node2", Groups: []string{"system:nodes"}}
   427  	node3 := &user.DefaultInfo{Name: "system:node:node3", Groups: []string{"system:nodes"}}
   428  
   429  	g.AddPod(&corev1.Pod{
   430  		ObjectMeta: metav1.ObjectMeta{Name: "pod1-node1", Namespace: "ns1"},
   431  		Spec: corev1.PodSpec{
   432  			NodeName: "node1",
   433  			Volumes: []corev1.Volume{
   434  				{VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "node1-only"}}},
   435  				{VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "node1-node2-only"}}},
   436  				{VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "shared-all"}}},
   437  			},
   438  		},
   439  	})
   440  	g.AddPod(&corev1.Pod{
   441  		ObjectMeta: metav1.ObjectMeta{Name: "pod2-node2", Namespace: "ns1"},
   442  		Spec: corev1.PodSpec{
   443  			NodeName: "node2",
   444  			Volumes: []corev1.Volume{
   445  				{VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "node1-node2-only"}}},
   446  				{VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "shared-all"}}},
   447  			},
   448  		},
   449  	})
   450  
   451  	pod3 := &corev1.Pod{
   452  		ObjectMeta: metav1.ObjectMeta{Name: "pod3-node3", Namespace: "ns1"},
   453  		Spec: corev1.PodSpec{
   454  			NodeName: "node3",
   455  			Volumes: []corev1.Volume{
   456  				{VolumeSource: corev1.VolumeSource{Secret: &corev1.SecretVolumeSource{SecretName: "shared-all"}}},
   457  			},
   458  		},
   459  	}
   460  	g.AddPod(pod3)
   461  
   462  	testcases := []struct {
   463  		User      user.Info
   464  		Secret    string
   465  		ConfigMap string
   466  		Decision  authorizer.Decision
   467  	}{
   468  		{User: node1, Decision: authorizer.DecisionAllow, Secret: "node1-only"},
   469  		{User: node1, Decision: authorizer.DecisionAllow, Secret: "node1-node2-only"},
   470  		{User: node1, Decision: authorizer.DecisionAllow, Secret: "shared-all"},
   471  
   472  		{User: node2, Decision: authorizer.DecisionNoOpinion, Secret: "node1-only"},
   473  		{User: node2, Decision: authorizer.DecisionAllow, Secret: "node1-node2-only"},
   474  		{User: node2, Decision: authorizer.DecisionAllow, Secret: "shared-all"},
   475  
   476  		{User: node3, Decision: authorizer.DecisionNoOpinion, Secret: "node1-only"},
   477  		{User: node3, Decision: authorizer.DecisionNoOpinion, Secret: "node1-node2-only"},
   478  		{User: node3, Decision: authorizer.DecisionAllow, Secret: "shared-all"},
   479  	}
   480  
   481  	for i, tc := range testcases {
   482  		var (
   483  			decision authorizer.Decision
   484  			err      error
   485  		)
   486  
   487  		if len(tc.Secret) > 0 {
   488  			decision, _, err = authz.Authorize(context.Background(), authorizer.AttributesRecord{User: tc.User, ResourceRequest: true, Verb: "get", Resource: "secrets", Namespace: "ns1", Name: tc.Secret})
   489  			if err != nil {
   490  				t.Errorf("%d: unexpected error: %v", i, err)
   491  				continue
   492  			}
   493  		} else if len(tc.ConfigMap) > 0 {
   494  			decision, _, err = authz.Authorize(context.Background(), authorizer.AttributesRecord{User: tc.User, ResourceRequest: true, Verb: "get", Resource: "configmaps", Namespace: "ns1", Name: tc.ConfigMap})
   495  			if err != nil {
   496  				t.Errorf("%d: unexpected error: %v", i, err)
   497  				continue
   498  			}
   499  		} else {
   500  			t.Fatalf("test case must include a request for a Secret or ConfigMap")
   501  		}
   502  
   503  		if decision != tc.Decision {
   504  			t.Errorf("%d: expected %v, got %v", i, tc.Decision, decision)
   505  		}
   506  	}
   507  
   508  	{
   509  		node3SharedSecretGet := authorizer.AttributesRecord{User: node3, ResourceRequest: true, Verb: "get", Resource: "secrets", Namespace: "ns1", Name: "shared-all"}
   510  
   511  		decision, _, err := authz.Authorize(context.Background(), node3SharedSecretGet)
   512  		if err != nil {
   513  			t.Errorf("unexpected error: %v", err)
   514  		}
   515  		if decision != authorizer.DecisionAllow {
   516  			t.Error("expected allowed")
   517  		}
   518  
   519  		// should trigger recalculation of the shared secret index
   520  		pod3.Spec.Volumes = nil
   521  		g.AddPod(pod3)
   522  
   523  		decision, _, err = authz.Authorize(context.Background(), node3SharedSecretGet)
   524  		if err != nil {
   525  			t.Errorf("unexpected error: %v", err)
   526  		}
   527  		if decision == authorizer.DecisionAllow {
   528  			t.Errorf("unexpectedly allowed")
   529  		}
   530  	}
   531  }
   532  
   533  type sampleDataOpts struct {
   534  	nodes       int
   535  	namespaces  int
   536  	podsPerNode int
   537  
   538  	attachmentsPerNode int
   539  
   540  	// sharedConfigMapsPerNamespaces defines number of shared configmaps in a given
   541  	// namespace. Each pod then mounts a random set of size `sharedConfigMapsPerPod`
   542  	// from that set. sharedConfigMapsPerPod is used if greater.
   543  	sharedConfigMapsPerNamespace int
   544  	// sharedSecretsPerNamespaces defines number of shared secrets in a given
   545  	// namespace. Each pod then mounts a random set of size `sharedSecretsPerPod`
   546  	// from that set. sharedSecretsPerPod is used if greater.
   547  	sharedSecretsPerNamespace int
   548  	// sharedPVCsPerNamespaces defines number of shared pvcs in a given
   549  	// namespace. Each pod then mounts a random set of size `sharedPVCsPerPod`
   550  	// from that set. sharedPVCsPerPod is used if greater.
   551  	sharedPVCsPerNamespace int
   552  
   553  	sharedConfigMapsPerPod int
   554  	sharedSecretsPerPod    int
   555  	sharedPVCsPerPod       int
   556  
   557  	uniqueSecretsPerPod                         int
   558  	uniqueConfigMapsPerPod                      int
   559  	uniquePVCsPerPod                            int
   560  	uniqueResourceClaimsPerPod                  int
   561  	uniqueResourceClaimTemplatesPerPod          int
   562  	uniqueResourceClaimTemplatesWithClaimPerPod int
   563  
   564  	nodeResourceCapacitiesPerNode int
   565  }
   566  
   567  func BenchmarkPopulationAllocation(b *testing.B) {
   568  	opts := &sampleDataOpts{
   569  		nodes:                  500,
   570  		namespaces:             200,
   571  		podsPerNode:            200,
   572  		attachmentsPerNode:     20,
   573  		sharedConfigMapsPerPod: 0,
   574  		uniqueConfigMapsPerPod: 1,
   575  		sharedSecretsPerPod:    1,
   576  		uniqueSecretsPerPod:    1,
   577  		sharedPVCsPerPod:       0,
   578  		uniquePVCsPerPod:       1,
   579  	}
   580  
   581  	nodes, pods, pvs, attachments, slices := generate(opts)
   582  	b.ResetTimer()
   583  
   584  	for i := 0; i < b.N; i++ {
   585  		g := NewGraph()
   586  		populate(g, nodes, pods, pvs, attachments, slices)
   587  	}
   588  }
   589  
   590  func BenchmarkPopulationRetention(b *testing.B) {
   591  
   592  	// Run with:
   593  	// go test ./plugin/pkg/auth/authorizer/node -benchmem -bench . -run None -v -o node.test -timeout 300m
   594  
   595  	// Evaluate retained memory with:
   596  	// go tool pprof --inuse_space node.test plugin/pkg/auth/authorizer/node/BenchmarkPopulationRetention.profile
   597  	// list populate
   598  
   599  	opts := &sampleDataOpts{
   600  		nodes:                  500,
   601  		namespaces:             200,
   602  		podsPerNode:            200,
   603  		attachmentsPerNode:     20,
   604  		sharedConfigMapsPerPod: 0,
   605  		uniqueConfigMapsPerPod: 1,
   606  		sharedSecretsPerPod:    1,
   607  		uniqueSecretsPerPod:    1,
   608  		sharedPVCsPerPod:       0,
   609  		uniquePVCsPerPod:       1,
   610  	}
   611  
   612  	nodes, pods, pvs, attachments, slices := generate(opts)
   613  	// Garbage collect before the first iteration
   614  	runtime.GC()
   615  	b.ResetTimer()
   616  
   617  	for i := 0; i < b.N; i++ {
   618  		g := NewGraph()
   619  		populate(g, nodes, pods, pvs, attachments, slices)
   620  
   621  		if i == 0 {
   622  			f, _ := os.Create("BenchmarkPopulationRetention.profile")
   623  			runtime.GC()
   624  			pprof.WriteHeapProfile(f)
   625  			f.Close()
   626  			// reference the graph to keep it from getting garbage collected
   627  			_ = fmt.Sprintf("%T\n", g)
   628  		}
   629  	}
   630  }
   631  
   632  func BenchmarkWriteIndexMaintenance(b *testing.B) {
   633  
   634  	// Run with:
   635  	// go test ./plugin/pkg/auth/authorizer/node -benchmem -bench BenchmarkWriteIndexMaintenance -run None
   636  
   637  	opts := &sampleDataOpts{
   638  		// simulate high replication in a small number of namespaces:
   639  		nodes:                  5000,
   640  		namespaces:             1,
   641  		podsPerNode:            1,
   642  		attachmentsPerNode:     20,
   643  		sharedConfigMapsPerPod: 0,
   644  		uniqueConfigMapsPerPod: 1,
   645  		sharedSecretsPerPod:    1,
   646  		uniqueSecretsPerPod:    1,
   647  		sharedPVCsPerPod:       0,
   648  		uniquePVCsPerPod:       1,
   649  	}
   650  	nodes, pods, pvs, attachments, slices := generate(opts)
   651  	g := NewGraph()
   652  	populate(g, nodes, pods, pvs, attachments, slices)
   653  	// Garbage collect before the first iteration
   654  	runtime.GC()
   655  	b.ResetTimer()
   656  
   657  	b.SetParallelism(100)
   658  	b.RunParallel(func(pb *testing.PB) {
   659  		for pb.Next() {
   660  			g.AddPod(pods[0])
   661  		}
   662  	})
   663  }
   664  
   665  func BenchmarkAuthorization(b *testing.B) {
   666  	g := NewGraph()
   667  
   668  	opts := &sampleDataOpts{
   669  		// To simulate high replication in a small number of namespaces:
   670  		// nodes:       5000,
   671  		// namespaces:  10,
   672  		// podsPerNode: 10,
   673  		nodes:                  500,
   674  		namespaces:             200,
   675  		podsPerNode:            200,
   676  		attachmentsPerNode:     20,
   677  		sharedConfigMapsPerPod: 0,
   678  		uniqueConfigMapsPerPod: 1,
   679  		sharedSecretsPerPod:    1,
   680  		uniqueSecretsPerPod:    1,
   681  		sharedPVCsPerPod:       0,
   682  		uniquePVCsPerPod:       1,
   683  	}
   684  	nodes, pods, pvs, attachments, slices := generate(opts)
   685  	populate(g, nodes, pods, pvs, attachments, slices)
   686  
   687  	identifier := nodeidentifier.NewDefaultNodeIdentifier()
   688  	authz := NewAuthorizer(g, identifier, bootstrappolicy.NodeRules())
   689  
   690  	node0 := &user.DefaultInfo{Name: "system:node:node0", Groups: []string{"system:nodes"}}
   691  
   692  	tests := []struct {
   693  		name     string
   694  		attrs    authorizer.AttributesRecord
   695  		expect   authorizer.Decision
   696  		features featuregate.FeatureGate
   697  	}{
   698  		{
   699  			name:   "allowed configmap",
   700  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node0", Namespace: "ns0"},
   701  			expect: authorizer.DecisionAllow,
   702  		},
   703  		{
   704  			name:   "allowed secret via pod",
   705  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node0", Namespace: "ns0"},
   706  			expect: authorizer.DecisionAllow,
   707  		},
   708  		{
   709  			name:   "allowed shared secret via pod",
   710  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-shared", Namespace: "ns0"},
   711  			expect: authorizer.DecisionAllow,
   712  		},
   713  		{
   714  			name:   "disallowed configmap",
   715  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "configmaps", Name: "configmap0-pod0-node1", Namespace: "ns0"},
   716  			expect: authorizer.DecisionNoOpinion,
   717  		},
   718  		{
   719  			name:   "disallowed secret via pod",
   720  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret0-pod0-node1", Namespace: "ns0"},
   721  			expect: authorizer.DecisionNoOpinion,
   722  		},
   723  		{
   724  			name:   "disallowed shared secret via pvc",
   725  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "secrets", Name: "secret-pv0-pod0-node1-ns0", Namespace: "ns0"},
   726  			expect: authorizer.DecisionNoOpinion,
   727  		},
   728  		{
   729  			name:   "disallowed pvc",
   730  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumeclaims", Name: "pvc0-pod0-node1", Namespace: "ns0"},
   731  			expect: authorizer.DecisionNoOpinion,
   732  		},
   733  		{
   734  			name:   "disallowed pv",
   735  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "persistentvolumes", Name: "pv0-pod0-node1-ns0", Namespace: ""},
   736  			expect: authorizer.DecisionNoOpinion,
   737  		},
   738  		{
   739  			name:   "disallowed attachment - no relationship",
   740  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "volumeattachments", APIGroup: "storage.k8s.io", Name: "attachment0-node1"},
   741  			expect: authorizer.DecisionNoOpinion,
   742  		},
   743  		{
   744  			name:   "allowed attachment",
   745  			attrs:  authorizer.AttributesRecord{User: node0, ResourceRequest: true, Verb: "get", Resource: "volumeattachments", APIGroup: "storage.k8s.io", Name: "attachment0-node0"},
   746  			expect: authorizer.DecisionAllow,
   747  		},
   748  	}
   749  
   750  	podToAdd, _ := generatePod("testwrite", "ns0", "node0", "default", opts)
   751  
   752  	b.ResetTimer()
   753  	for _, testWriteContention := range []bool{false, true} {
   754  
   755  		shouldWrite := int32(1)
   756  		writes := int64(0)
   757  		_1ms := int64(0)
   758  		_10ms := int64(0)
   759  		_25ms := int64(0)
   760  		_50ms := int64(0)
   761  		_100ms := int64(0)
   762  		_250ms := int64(0)
   763  		_500ms := int64(0)
   764  		_1000ms := int64(0)
   765  		_1s := int64(0)
   766  
   767  		contentionPrefix := ""
   768  		if testWriteContention {
   769  			contentionPrefix = "contentious "
   770  			// Start a writer pushing graph modifications 100x a second
   771  			go func() {
   772  				for shouldWrite == 1 {
   773  					go func() {
   774  						start := time.Now()
   775  						authz.graph.AddPod(podToAdd)
   776  						diff := time.Since(start)
   777  						atomic.AddInt64(&writes, 1)
   778  						switch {
   779  						case diff < time.Millisecond:
   780  							atomic.AddInt64(&_1ms, 1)
   781  						case diff < 10*time.Millisecond:
   782  							atomic.AddInt64(&_10ms, 1)
   783  						case diff < 25*time.Millisecond:
   784  							atomic.AddInt64(&_25ms, 1)
   785  						case diff < 50*time.Millisecond:
   786  							atomic.AddInt64(&_50ms, 1)
   787  						case diff < 100*time.Millisecond:
   788  							atomic.AddInt64(&_100ms, 1)
   789  						case diff < 250*time.Millisecond:
   790  							atomic.AddInt64(&_250ms, 1)
   791  						case diff < 500*time.Millisecond:
   792  							atomic.AddInt64(&_500ms, 1)
   793  						case diff < 1000*time.Millisecond:
   794  							atomic.AddInt64(&_1000ms, 1)
   795  						default:
   796  							atomic.AddInt64(&_1s, 1)
   797  						}
   798  					}()
   799  					time.Sleep(10 * time.Millisecond)
   800  				}
   801  			}()
   802  		}
   803  
   804  		for _, tc := range tests {
   805  			if tc.features == nil {
   806  				authz.features = utilfeature.DefaultFeatureGate
   807  			} else {
   808  				authz.features = tc.features
   809  			}
   810  			b.Run(contentionPrefix+tc.name, func(b *testing.B) {
   811  				// Run authorization checks in parallel
   812  				b.SetParallelism(5000)
   813  				b.RunParallel(func(pb *testing.PB) {
   814  					for pb.Next() {
   815  						decision, _, _ := authz.Authorize(context.Background(), tc.attrs)
   816  						if decision != tc.expect {
   817  							b.Errorf("expected %v, got %v", tc.expect, decision)
   818  						}
   819  					}
   820  				})
   821  			})
   822  		}
   823  
   824  		atomic.StoreInt32(&shouldWrite, 0)
   825  		if testWriteContention {
   826  			b.Logf("graph modifications during contention test: %d", writes)
   827  			b.Logf("<1ms=%d, <10ms=%d, <25ms=%d, <50ms=%d, <100ms=%d, <250ms=%d, <500ms=%d, <1000ms=%d, >1000ms=%d", _1ms, _10ms, _25ms, _50ms, _100ms, _250ms, _500ms, _1000ms, _1s)
   828  		} else {
   829  			b.Logf("graph modifications during non-contention test: %d", writes)
   830  		}
   831  	}
   832  }
   833  
   834  func populate(graph *Graph, nodes []*corev1.Node, pods []*corev1.Pod, pvs []*corev1.PersistentVolume, attachments []*storagev1.VolumeAttachment, slices []*resourcev1alpha2.ResourceSlice) {
   835  	p := &graphPopulator{}
   836  	p.graph = graph
   837  	for _, pod := range pods {
   838  		p.addPod(pod)
   839  	}
   840  	for _, pv := range pvs {
   841  		p.addPV(pv)
   842  	}
   843  	for _, attachment := range attachments {
   844  		p.addVolumeAttachment(attachment)
   845  	}
   846  	for _, slice := range slices {
   847  		p.addResourceSlice(slice)
   848  	}
   849  }
   850  
   851  func randomSubset(a, b int) []int {
   852  	if b < a {
   853  		b = a
   854  	}
   855  	return rand.Perm(b)[:a]
   856  }
   857  
   858  // generate creates sample pods and persistent volumes based on the provided options.
   859  // the secret/configmap/pvc/node references in the pod and pv objects are named to indicate the connections between the objects.
   860  // for example, secret0-pod0-node0 is a secret referenced by pod0 which is bound to node0.
   861  // when populated into the graph, the node authorizer should allow node0 to access that secret, but not node1.
   862  func generate(opts *sampleDataOpts) ([]*corev1.Node, []*corev1.Pod, []*corev1.PersistentVolume, []*storagev1.VolumeAttachment, []*resourcev1alpha2.ResourceSlice) {
   863  	nodes := make([]*corev1.Node, 0, opts.nodes)
   864  	pods := make([]*corev1.Pod, 0, opts.nodes*opts.podsPerNode)
   865  	pvs := make([]*corev1.PersistentVolume, 0, (opts.nodes*opts.podsPerNode*opts.uniquePVCsPerPod)+(opts.sharedPVCsPerPod*opts.namespaces))
   866  	attachments := make([]*storagev1.VolumeAttachment, 0, opts.nodes*opts.attachmentsPerNode)
   867  	slices := make([]*resourcev1alpha2.ResourceSlice, 0, opts.nodes*opts.nodeResourceCapacitiesPerNode)
   868  
   869  	rand.Seed(12345)
   870  
   871  	for n := 0; n < opts.nodes; n++ {
   872  		nodeName := fmt.Sprintf("node%d", n)
   873  		for p := 0; p < opts.podsPerNode; p++ {
   874  			name := fmt.Sprintf("pod%d-%s", p, nodeName)
   875  			namespace := fmt.Sprintf("ns%d", p%opts.namespaces)
   876  			svcAccountName := fmt.Sprintf("svcacct%d-%s", p, nodeName)
   877  
   878  			pod, podPVs := generatePod(name, namespace, nodeName, svcAccountName, opts)
   879  			pods = append(pods, pod)
   880  			pvs = append(pvs, podPVs...)
   881  		}
   882  		for a := 0; a < opts.attachmentsPerNode; a++ {
   883  			attachment := &storagev1.VolumeAttachment{}
   884  			attachment.Name = fmt.Sprintf("attachment%d-%s", a, nodeName)
   885  			attachment.Spec.NodeName = nodeName
   886  			attachments = append(attachments, attachment)
   887  		}
   888  
   889  		nodes = append(nodes, &corev1.Node{
   890  			ObjectMeta: metav1.ObjectMeta{Name: nodeName},
   891  			Spec:       corev1.NodeSpec{},
   892  		})
   893  
   894  		for p := 0; p <= opts.nodeResourceCapacitiesPerNode; p++ {
   895  			name := fmt.Sprintf("slice%d-%s", p, nodeName)
   896  			slice := &resourcev1alpha2.ResourceSlice{
   897  				ObjectMeta: metav1.ObjectMeta{Name: name},
   898  				NodeName:   nodeName,
   899  			}
   900  			slices = append(slices, slice)
   901  		}
   902  	}
   903  	return nodes, pods, pvs, attachments, slices
   904  }
   905  
   906  func generatePod(name, namespace, nodeName, svcAccountName string, opts *sampleDataOpts) (*corev1.Pod, []*corev1.PersistentVolume) {
   907  	pvs := make([]*corev1.PersistentVolume, 0, opts.uniquePVCsPerPod+opts.sharedPVCsPerPod)
   908  
   909  	pod := &corev1.Pod{}
   910  	pod.Name = name
   911  	pod.Namespace = namespace
   912  	pod.Spec.NodeName = nodeName
   913  	pod.Spec.ServiceAccountName = svcAccountName
   914  
   915  	for i := 0; i < opts.uniqueSecretsPerPod; i++ {
   916  		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{
   917  			Secret: &corev1.SecretVolumeSource{SecretName: fmt.Sprintf("secret%d-%s", i, pod.Name)},
   918  		}})
   919  	}
   920  	// Choose shared secrets randomly from shared secrets in a namespace.
   921  	subset := randomSubset(opts.sharedSecretsPerPod, opts.sharedSecretsPerNamespace)
   922  	for _, i := range subset {
   923  		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{
   924  			Secret: &corev1.SecretVolumeSource{SecretName: fmt.Sprintf("secret%d-shared", i)},
   925  		}})
   926  	}
   927  
   928  	for i := 0; i < opts.uniqueConfigMapsPerPod; i++ {
   929  		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{
   930  			ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: fmt.Sprintf("configmap%d-%s", i, pod.Name)}},
   931  		}})
   932  	}
   933  	// Choose shared configmaps randomly from shared configmaps in a namespace.
   934  	subset = randomSubset(opts.sharedConfigMapsPerPod, opts.sharedConfigMapsPerNamespace)
   935  	for _, i := range subset {
   936  		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{
   937  			ConfigMap: &corev1.ConfigMapVolumeSource{LocalObjectReference: corev1.LocalObjectReference{Name: fmt.Sprintf("configmap%d-shared", i)}},
   938  		}})
   939  	}
   940  
   941  	for i := 0; i < opts.uniquePVCsPerPod; i++ {
   942  		pv := &corev1.PersistentVolume{}
   943  		pv.Name = fmt.Sprintf("pv%d-%s-%s", i, pod.Name, pod.Namespace)
   944  		pv.Spec.FlexVolume = &corev1.FlexPersistentVolumeSource{SecretRef: &corev1.SecretReference{Name: fmt.Sprintf("secret-%s", pv.Name)}}
   945  		pv.Spec.ClaimRef = &corev1.ObjectReference{Name: fmt.Sprintf("pvc%d-%s", i, pod.Name), Namespace: pod.Namespace}
   946  		pvs = append(pvs, pv)
   947  
   948  		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{
   949  			PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pv.Spec.ClaimRef.Name},
   950  		}})
   951  	}
   952  	for i := 0; i < opts.uniqueResourceClaimsPerPod; i++ {
   953  		claimName := fmt.Sprintf("claim%d-%s-%s", i, pod.Name, pod.Namespace)
   954  		pod.Spec.ResourceClaims = append(pod.Spec.ResourceClaims, corev1.PodResourceClaim{
   955  			Name: fmt.Sprintf("claim%d", i),
   956  			Source: corev1.ClaimSource{
   957  				ResourceClaimName: &claimName,
   958  			},
   959  		})
   960  	}
   961  	for i := 0; i < opts.uniqueResourceClaimTemplatesPerPod; i++ {
   962  		claimTemplateName := fmt.Sprintf("claimtemplate%d-%s-%s", i, pod.Name, pod.Namespace)
   963  		podClaimName := fmt.Sprintf("claimtemplate%d", i)
   964  		pod.Spec.ResourceClaims = append(pod.Spec.ResourceClaims, corev1.PodResourceClaim{
   965  			Name: podClaimName,
   966  			Source: corev1.ClaimSource{
   967  				ResourceClaimTemplateName: &claimTemplateName,
   968  			},
   969  		})
   970  	}
   971  	for i := 0; i < opts.uniqueResourceClaimTemplatesWithClaimPerPod; i++ {
   972  		claimTemplateName := fmt.Sprintf("claimtemplate%d-%s-%s", i, pod.Name, pod.Namespace)
   973  		podClaimName := fmt.Sprintf("claimtemplate-with-claim%d", i)
   974  		claimName := fmt.Sprintf("generated-claim-%s-%s-%d", pod.Name, pod.Namespace, i)
   975  		pod.Spec.ResourceClaims = append(pod.Spec.ResourceClaims, corev1.PodResourceClaim{
   976  			Name: podClaimName,
   977  			Source: corev1.ClaimSource{
   978  				ResourceClaimTemplateName: &claimTemplateName,
   979  			},
   980  		})
   981  		pod.Status.ResourceClaimStatuses = append(pod.Status.ResourceClaimStatuses, corev1.PodResourceClaimStatus{
   982  			Name:              podClaimName,
   983  			ResourceClaimName: &claimName,
   984  		})
   985  	}
   986  	// Choose shared pvcs randomly from shared pvcs in a namespace.
   987  	subset = randomSubset(opts.sharedPVCsPerPod, opts.sharedPVCsPerNamespace)
   988  	for _, i := range subset {
   989  		pv := &corev1.PersistentVolume{}
   990  		pv.Name = fmt.Sprintf("pv%d-shared-%s", i, pod.Namespace)
   991  		pv.Spec.FlexVolume = &corev1.FlexPersistentVolumeSource{SecretRef: &corev1.SecretReference{Name: fmt.Sprintf("secret-%s", pv.Name)}}
   992  		pv.Spec.ClaimRef = &corev1.ObjectReference{Name: fmt.Sprintf("pvc%d-shared", i), Namespace: pod.Namespace}
   993  		pvs = append(pvs, pv)
   994  
   995  		pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{VolumeSource: corev1.VolumeSource{
   996  			PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pv.Spec.ClaimRef.Name},
   997  		}})
   998  	}
   999  
  1000  	return pod, pvs
  1001  }
  1002  

View as plain text