...

Source file src/k8s.io/kubernetes/pkg/volume/projected/projected_test.go

Documentation: k8s.io/kubernetes/pkg/volume/projected

     1  /*
     2  Copyright 2015 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 projected
    18  
    19  import (
    20  	"crypto/ed25519"
    21  	"crypto/rand"
    22  	"crypto/x509"
    23  	"crypto/x509/pkix"
    24  	"encoding/pem"
    25  	"fmt"
    26  	"math/big"
    27  	"os"
    28  	"path/filepath"
    29  	"reflect"
    30  	"strings"
    31  	"testing"
    32  
    33  	"github.com/google/go-cmp/cmp"
    34  	authenticationv1 "k8s.io/api/authentication/v1"
    35  	certificatesv1alpha1 "k8s.io/api/certificates/v1alpha1"
    36  	v1 "k8s.io/api/core/v1"
    37  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    38  	"k8s.io/apimachinery/pkg/runtime"
    39  	"k8s.io/apimachinery/pkg/types"
    40  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    41  	clientset "k8s.io/client-go/kubernetes"
    42  	"k8s.io/client-go/kubernetes/fake"
    43  	clitesting "k8s.io/client-go/testing"
    44  	pkgauthenticationv1 "k8s.io/kubernetes/pkg/apis/authentication/v1"
    45  	pkgcorev1 "k8s.io/kubernetes/pkg/apis/core/v1"
    46  	"k8s.io/kubernetes/pkg/volume"
    47  	"k8s.io/kubernetes/pkg/volume/emptydir"
    48  	volumetest "k8s.io/kubernetes/pkg/volume/testing"
    49  	"k8s.io/kubernetes/pkg/volume/util"
    50  	utilptr "k8s.io/utils/pointer"
    51  )
    52  
    53  func TestCollectDataWithSecret(t *testing.T) {
    54  	caseMappingMode := int32(0400)
    55  	cases := []struct {
    56  		name     string
    57  		mappings []v1.KeyToPath
    58  		secret   *v1.Secret
    59  		mode     int32
    60  		optional bool
    61  		payload  map[string]util.FileProjection
    62  		success  bool
    63  	}{
    64  		{
    65  			name: "no overrides",
    66  			secret: &v1.Secret{
    67  				Data: map[string][]byte{
    68  					"foo": []byte("foo"),
    69  					"bar": []byte("bar"),
    70  				},
    71  			},
    72  			mode: 0644,
    73  			payload: map[string]util.FileProjection{
    74  				"foo": {Data: []byte("foo"), Mode: 0644},
    75  				"bar": {Data: []byte("bar"), Mode: 0644},
    76  			},
    77  			success: true,
    78  		},
    79  		{
    80  			name: "basic 1",
    81  			mappings: []v1.KeyToPath{
    82  				{
    83  					Key:  "foo",
    84  					Path: "path/to/foo.txt",
    85  				},
    86  			},
    87  			secret: &v1.Secret{
    88  				Data: map[string][]byte{
    89  					"foo": []byte("foo"),
    90  					"bar": []byte("bar"),
    91  				},
    92  			},
    93  			mode: 0644,
    94  			payload: map[string]util.FileProjection{
    95  				"path/to/foo.txt": {Data: []byte("foo"), Mode: 0644},
    96  			},
    97  			success: true,
    98  		},
    99  		{
   100  			name: "subdirs",
   101  			mappings: []v1.KeyToPath{
   102  				{
   103  					Key:  "foo",
   104  					Path: "path/to/1/2/3/foo.txt",
   105  				},
   106  			},
   107  			secret: &v1.Secret{
   108  				Data: map[string][]byte{
   109  					"foo": []byte("foo"),
   110  					"bar": []byte("bar"),
   111  				},
   112  			},
   113  			mode: 0644,
   114  			payload: map[string]util.FileProjection{
   115  				"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
   116  			},
   117  			success: true,
   118  		},
   119  		{
   120  			name: "subdirs 2",
   121  			mappings: []v1.KeyToPath{
   122  				{
   123  					Key:  "foo",
   124  					Path: "path/to/1/2/3/foo.txt",
   125  				},
   126  			},
   127  			secret: &v1.Secret{
   128  				Data: map[string][]byte{
   129  					"foo": []byte("foo"),
   130  					"bar": []byte("bar"),
   131  				},
   132  			},
   133  			mode: 0644,
   134  			payload: map[string]util.FileProjection{
   135  				"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
   136  			},
   137  			success: true,
   138  		},
   139  		{
   140  			name: "subdirs 3",
   141  			mappings: []v1.KeyToPath{
   142  				{
   143  					Key:  "foo",
   144  					Path: "path/to/1/2/3/foo.txt",
   145  				},
   146  				{
   147  					Key:  "bar",
   148  					Path: "another/path/to/the/esteemed/bar.bin",
   149  				},
   150  			},
   151  			secret: &v1.Secret{
   152  				Data: map[string][]byte{
   153  					"foo": []byte("foo"),
   154  					"bar": []byte("bar"),
   155  				},
   156  			},
   157  			mode: 0644,
   158  			payload: map[string]util.FileProjection{
   159  				"path/to/1/2/3/foo.txt":                {Data: []byte("foo"), Mode: 0644},
   160  				"another/path/to/the/esteemed/bar.bin": {Data: []byte("bar"), Mode: 0644},
   161  			},
   162  			success: true,
   163  		},
   164  		{
   165  			name: "non existent key",
   166  			mappings: []v1.KeyToPath{
   167  				{
   168  					Key:  "zab",
   169  					Path: "path/to/foo.txt",
   170  				},
   171  			},
   172  			secret: &v1.Secret{
   173  				Data: map[string][]byte{
   174  					"foo": []byte("foo"),
   175  					"bar": []byte("bar"),
   176  				},
   177  			},
   178  			mode:    0644,
   179  			success: false,
   180  		},
   181  		{
   182  			name: "mapping with Mode",
   183  			mappings: []v1.KeyToPath{
   184  				{
   185  					Key:  "foo",
   186  					Path: "foo.txt",
   187  					Mode: &caseMappingMode,
   188  				},
   189  				{
   190  					Key:  "bar",
   191  					Path: "bar.bin",
   192  					Mode: &caseMappingMode,
   193  				},
   194  			},
   195  			secret: &v1.Secret{
   196  				Data: map[string][]byte{
   197  					"foo": []byte("foo"),
   198  					"bar": []byte("bar"),
   199  				},
   200  			},
   201  			mode: 0644,
   202  			payload: map[string]util.FileProjection{
   203  				"foo.txt": {Data: []byte("foo"), Mode: caseMappingMode},
   204  				"bar.bin": {Data: []byte("bar"), Mode: caseMappingMode},
   205  			},
   206  			success: true,
   207  		},
   208  		{
   209  			name: "mapping with defaultMode",
   210  			mappings: []v1.KeyToPath{
   211  				{
   212  					Key:  "foo",
   213  					Path: "foo.txt",
   214  				},
   215  				{
   216  					Key:  "bar",
   217  					Path: "bar.bin",
   218  				},
   219  			},
   220  			secret: &v1.Secret{
   221  				Data: map[string][]byte{
   222  					"foo": []byte("foo"),
   223  					"bar": []byte("bar"),
   224  				},
   225  			},
   226  			mode: 0644,
   227  			payload: map[string]util.FileProjection{
   228  				"foo.txt": {Data: []byte("foo"), Mode: 0644},
   229  				"bar.bin": {Data: []byte("bar"), Mode: 0644},
   230  			},
   231  			success: true,
   232  		},
   233  		{
   234  			name: "optional non existent key",
   235  			mappings: []v1.KeyToPath{
   236  				{
   237  					Key:  "zab",
   238  					Path: "path/to/foo.txt",
   239  				},
   240  			},
   241  			secret: &v1.Secret{
   242  				Data: map[string][]byte{
   243  					"foo": []byte("foo"),
   244  					"bar": []byte("bar"),
   245  				},
   246  			},
   247  			mode:     0644,
   248  			optional: true,
   249  			payload:  map[string]util.FileProjection{},
   250  			success:  true,
   251  		},
   252  	}
   253  
   254  	for _, tc := range cases {
   255  		t.Run(tc.name, func(t *testing.T) {
   256  
   257  			testNamespace := "test_projected_namespace"
   258  			tc.secret.ObjectMeta = metav1.ObjectMeta{
   259  				Namespace: testNamespace,
   260  				Name:      tc.name,
   261  			}
   262  
   263  			source := makeProjection(tc.name, utilptr.Int32Ptr(tc.mode), "secret")
   264  			source.Sources[0].Secret.Items = tc.mappings
   265  			source.Sources[0].Secret.Optional = &tc.optional
   266  
   267  			testPodUID := types.UID("test_pod_uid")
   268  			pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
   269  			client := fake.NewSimpleClientset(tc.secret)
   270  			tempDir, host := newTestHost(t, client)
   271  			defer os.RemoveAll(tempDir)
   272  			var myVolumeMounter = projectedVolumeMounter{
   273  				projectedVolume: &projectedVolume{
   274  					sources: source.Sources,
   275  					podUID:  pod.UID,
   276  					plugin: &projectedPlugin{
   277  						host:      host,
   278  						getSecret: host.GetSecretFunc(),
   279  					},
   280  				},
   281  				source: *source,
   282  				pod:    pod,
   283  			}
   284  
   285  			actualPayload, err := myVolumeMounter.collectData(volume.MounterArgs{})
   286  			if err != nil && tc.success {
   287  				t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
   288  				return
   289  			}
   290  			if err == nil && !tc.success {
   291  				t.Errorf("%v: unexpected success making payload", tc.name)
   292  				return
   293  			}
   294  			if !tc.success {
   295  				return
   296  			}
   297  			if e, a := tc.payload, actualPayload; !reflect.DeepEqual(e, a) {
   298  				t.Errorf("%v: expected and actual payload do not match", tc.name)
   299  			}
   300  		})
   301  	}
   302  }
   303  
   304  func TestCollectDataWithConfigMap(t *testing.T) {
   305  	caseMappingMode := int32(0400)
   306  	cases := []struct {
   307  		name      string
   308  		mappings  []v1.KeyToPath
   309  		configMap *v1.ConfigMap
   310  		mode      int32
   311  		optional  bool
   312  		payload   map[string]util.FileProjection
   313  		success   bool
   314  	}{
   315  		{
   316  			name: "no overrides",
   317  			configMap: &v1.ConfigMap{
   318  				Data: map[string]string{
   319  					"foo": "foo",
   320  					"bar": "bar",
   321  				},
   322  			},
   323  			mode: 0644,
   324  			payload: map[string]util.FileProjection{
   325  				"foo": {Data: []byte("foo"), Mode: 0644},
   326  				"bar": {Data: []byte("bar"), Mode: 0644},
   327  			},
   328  			success: true,
   329  		},
   330  		{
   331  			name: "basic 1",
   332  			mappings: []v1.KeyToPath{
   333  				{
   334  					Key:  "foo",
   335  					Path: "path/to/foo.txt",
   336  				},
   337  			},
   338  			configMap: &v1.ConfigMap{
   339  				Data: map[string]string{
   340  					"foo": "foo",
   341  					"bar": "bar",
   342  				},
   343  			},
   344  			mode: 0644,
   345  			payload: map[string]util.FileProjection{
   346  				"path/to/foo.txt": {Data: []byte("foo"), Mode: 0644},
   347  			},
   348  			success: true,
   349  		},
   350  		{
   351  			name: "subdirs",
   352  			mappings: []v1.KeyToPath{
   353  				{
   354  					Key:  "foo",
   355  					Path: "path/to/1/2/3/foo.txt",
   356  				},
   357  			},
   358  			configMap: &v1.ConfigMap{
   359  				Data: map[string]string{
   360  					"foo": "foo",
   361  					"bar": "bar",
   362  				},
   363  			},
   364  			mode: 0644,
   365  			payload: map[string]util.FileProjection{
   366  				"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
   367  			},
   368  			success: true,
   369  		},
   370  		{
   371  			name: "subdirs 2",
   372  			mappings: []v1.KeyToPath{
   373  				{
   374  					Key:  "foo",
   375  					Path: "path/to/1/2/3/foo.txt",
   376  				},
   377  			},
   378  			configMap: &v1.ConfigMap{
   379  				Data: map[string]string{
   380  					"foo": "foo",
   381  					"bar": "bar",
   382  				},
   383  			},
   384  			mode: 0644,
   385  			payload: map[string]util.FileProjection{
   386  				"path/to/1/2/3/foo.txt": {Data: []byte("foo"), Mode: 0644},
   387  			},
   388  			success: true,
   389  		},
   390  		{
   391  			name: "subdirs 3",
   392  			mappings: []v1.KeyToPath{
   393  				{
   394  					Key:  "foo",
   395  					Path: "path/to/1/2/3/foo.txt",
   396  				},
   397  				{
   398  					Key:  "bar",
   399  					Path: "another/path/to/the/esteemed/bar.bin",
   400  				},
   401  			},
   402  			configMap: &v1.ConfigMap{
   403  				Data: map[string]string{
   404  					"foo": "foo",
   405  					"bar": "bar",
   406  				},
   407  			},
   408  			mode: 0644,
   409  			payload: map[string]util.FileProjection{
   410  				"path/to/1/2/3/foo.txt":                {Data: []byte("foo"), Mode: 0644},
   411  				"another/path/to/the/esteemed/bar.bin": {Data: []byte("bar"), Mode: 0644},
   412  			},
   413  			success: true,
   414  		},
   415  		{
   416  			name: "non existent key",
   417  			mappings: []v1.KeyToPath{
   418  				{
   419  					Key:  "zab",
   420  					Path: "path/to/foo.txt",
   421  				},
   422  			},
   423  			configMap: &v1.ConfigMap{
   424  				Data: map[string]string{
   425  					"foo": "foo",
   426  					"bar": "bar",
   427  				},
   428  			},
   429  			mode:    0644,
   430  			success: false,
   431  		},
   432  		{
   433  			name: "mapping with Mode",
   434  			mappings: []v1.KeyToPath{
   435  				{
   436  					Key:  "foo",
   437  					Path: "foo.txt",
   438  					Mode: &caseMappingMode,
   439  				},
   440  				{
   441  					Key:  "bar",
   442  					Path: "bar.bin",
   443  					Mode: &caseMappingMode,
   444  				},
   445  			},
   446  			configMap: &v1.ConfigMap{
   447  				Data: map[string]string{
   448  					"foo": "foo",
   449  					"bar": "bar",
   450  				},
   451  			},
   452  			mode: 0644,
   453  			payload: map[string]util.FileProjection{
   454  				"foo.txt": {Data: []byte("foo"), Mode: caseMappingMode},
   455  				"bar.bin": {Data: []byte("bar"), Mode: caseMappingMode},
   456  			},
   457  			success: true,
   458  		},
   459  		{
   460  			name: "mapping with defaultMode",
   461  			mappings: []v1.KeyToPath{
   462  				{
   463  					Key:  "foo",
   464  					Path: "foo.txt",
   465  				},
   466  				{
   467  					Key:  "bar",
   468  					Path: "bar.bin",
   469  				},
   470  			},
   471  			configMap: &v1.ConfigMap{
   472  				Data: map[string]string{
   473  					"foo": "foo",
   474  					"bar": "bar",
   475  				},
   476  			},
   477  			mode: 0644,
   478  			payload: map[string]util.FileProjection{
   479  				"foo.txt": {Data: []byte("foo"), Mode: 0644},
   480  				"bar.bin": {Data: []byte("bar"), Mode: 0644},
   481  			},
   482  			success: true,
   483  		},
   484  		{
   485  			name: "optional non existent key",
   486  			mappings: []v1.KeyToPath{
   487  				{
   488  					Key:  "zab",
   489  					Path: "path/to/foo.txt",
   490  				},
   491  			},
   492  			configMap: &v1.ConfigMap{
   493  				Data: map[string]string{
   494  					"foo": "foo",
   495  					"bar": "bar",
   496  				},
   497  			},
   498  			mode:     0644,
   499  			optional: true,
   500  			payload:  map[string]util.FileProjection{},
   501  			success:  true,
   502  		},
   503  	}
   504  	for _, tc := range cases {
   505  		t.Run(tc.name, func(t *testing.T) {
   506  			testNamespace := "test_projected_namespace"
   507  			tc.configMap.ObjectMeta = metav1.ObjectMeta{
   508  				Namespace: testNamespace,
   509  				Name:      tc.name,
   510  			}
   511  
   512  			source := makeProjection(tc.name, utilptr.Int32Ptr(tc.mode), "configMap")
   513  			source.Sources[0].ConfigMap.Items = tc.mappings
   514  			source.Sources[0].ConfigMap.Optional = &tc.optional
   515  
   516  			testPodUID := types.UID("test_pod_uid")
   517  			pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
   518  			client := fake.NewSimpleClientset(tc.configMap)
   519  			tempDir, host := newTestHost(t, client)
   520  			defer os.RemoveAll(tempDir)
   521  			var myVolumeMounter = projectedVolumeMounter{
   522  				projectedVolume: &projectedVolume{
   523  					sources: source.Sources,
   524  					podUID:  pod.UID,
   525  					plugin: &projectedPlugin{
   526  						host:         host,
   527  						getConfigMap: host.GetConfigMapFunc(),
   528  					},
   529  				},
   530  				source: *source,
   531  				pod:    pod,
   532  			}
   533  
   534  			actualPayload, err := myVolumeMounter.collectData(volume.MounterArgs{})
   535  			if err != nil && tc.success {
   536  				t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
   537  				return
   538  			}
   539  			if err == nil && !tc.success {
   540  				t.Errorf("%v: unexpected success making payload", tc.name)
   541  				return
   542  			}
   543  			if !tc.success {
   544  				return
   545  			}
   546  			if e, a := tc.payload, actualPayload; !reflect.DeepEqual(e, a) {
   547  				t.Errorf("%v: expected and actual payload do not match", tc.name)
   548  			}
   549  		})
   550  	}
   551  }
   552  
   553  func TestCollectDataWithDownwardAPI(t *testing.T) {
   554  	testNamespace := "test_projected_namespace"
   555  	testPodUID := types.UID("test_pod_uid")
   556  	testPodName := "podName"
   557  
   558  	cases := []struct {
   559  		name       string
   560  		volumeFile []v1.DownwardAPIVolumeFile
   561  		pod        *v1.Pod
   562  		mode       int32
   563  		payload    map[string]util.FileProjection
   564  		success    bool
   565  	}{
   566  		{
   567  			name: "annotation",
   568  			volumeFile: []v1.DownwardAPIVolumeFile{
   569  				{Path: "annotation", FieldRef: &v1.ObjectFieldSelector{
   570  					FieldPath: "metadata.annotations['a1']"}}},
   571  			pod: &v1.Pod{
   572  				ObjectMeta: metav1.ObjectMeta{
   573  					Name:      testPodName,
   574  					Namespace: testNamespace,
   575  					Annotations: map[string]string{
   576  						"a1": "value1",
   577  						"a2": "value2",
   578  					},
   579  					UID: testPodUID},
   580  			},
   581  			mode: 0644,
   582  			payload: map[string]util.FileProjection{
   583  				"annotation": {Data: []byte("value1"), Mode: 0644},
   584  			},
   585  			success: true,
   586  		},
   587  		{
   588  			name: "annotation-error",
   589  			volumeFile: []v1.DownwardAPIVolumeFile{
   590  				{Path: "annotation", FieldRef: &v1.ObjectFieldSelector{
   591  					FieldPath: "metadata.annotations['']"}}},
   592  			pod: &v1.Pod{
   593  				ObjectMeta: metav1.ObjectMeta{
   594  					Name:      testPodName,
   595  					Namespace: testNamespace,
   596  					Annotations: map[string]string{
   597  						"a1": "value1",
   598  						"a2": "value2",
   599  					},
   600  					UID: testPodUID},
   601  			},
   602  			mode: 0644,
   603  			payload: map[string]util.FileProjection{
   604  				"annotation": {Data: []byte("does-not-matter-because-this-test-case-will-fail-anyway"), Mode: 0644},
   605  			},
   606  			success: false,
   607  		},
   608  		{
   609  			name: "labels",
   610  			volumeFile: []v1.DownwardAPIVolumeFile{
   611  				{Path: "labels", FieldRef: &v1.ObjectFieldSelector{
   612  					FieldPath: "metadata.labels"}}},
   613  			pod: &v1.Pod{
   614  				ObjectMeta: metav1.ObjectMeta{
   615  					Name:      testPodName,
   616  					Namespace: testNamespace,
   617  					Labels: map[string]string{
   618  						"key1": "value1",
   619  						"key2": "value2"},
   620  					UID: testPodUID},
   621  			},
   622  			mode: 0644,
   623  			payload: map[string]util.FileProjection{
   624  				"labels": {Data: []byte("key1=\"value1\"\nkey2=\"value2\""), Mode: 0644},
   625  			},
   626  			success: true,
   627  		},
   628  		{
   629  			name: "annotations",
   630  			volumeFile: []v1.DownwardAPIVolumeFile{
   631  				{Path: "annotations", FieldRef: &v1.ObjectFieldSelector{
   632  					FieldPath: "metadata.annotations"}}},
   633  			pod: &v1.Pod{
   634  				ObjectMeta: metav1.ObjectMeta{
   635  					Name:      testPodName,
   636  					Namespace: testNamespace,
   637  					Annotations: map[string]string{
   638  						"a1": "value1",
   639  						"a2": "value2"},
   640  					UID: testPodUID},
   641  			},
   642  			mode: 0644,
   643  			payload: map[string]util.FileProjection{
   644  				"annotations": {Data: []byte("a1=\"value1\"\na2=\"value2\""), Mode: 0644},
   645  			},
   646  			success: true,
   647  		},
   648  		{
   649  			name: "name",
   650  			volumeFile: []v1.DownwardAPIVolumeFile{
   651  				{Path: "name_file_name", FieldRef: &v1.ObjectFieldSelector{
   652  					FieldPath: "metadata.name"}}},
   653  			pod: &v1.Pod{
   654  				ObjectMeta: metav1.ObjectMeta{
   655  					Name:      testPodName,
   656  					Namespace: testNamespace,
   657  					UID:       testPodUID},
   658  			},
   659  			mode: 0644,
   660  			payload: map[string]util.FileProjection{
   661  				"name_file_name": {Data: []byte(testPodName), Mode: 0644},
   662  			},
   663  			success: true,
   664  		},
   665  		{
   666  			name: "namespace",
   667  			volumeFile: []v1.DownwardAPIVolumeFile{
   668  				{Path: "namespace_file_name", FieldRef: &v1.ObjectFieldSelector{
   669  					FieldPath: "metadata.namespace"}}},
   670  			pod: &v1.Pod{
   671  				ObjectMeta: metav1.ObjectMeta{
   672  					Name:      testPodName,
   673  					Namespace: testNamespace,
   674  					UID:       testPodUID},
   675  			},
   676  			mode: 0644,
   677  			payload: map[string]util.FileProjection{
   678  				"namespace_file_name": {Data: []byte(testNamespace), Mode: 0644},
   679  			},
   680  			success: true,
   681  		},
   682  	}
   683  
   684  	for _, tc := range cases {
   685  		t.Run(tc.name, func(t *testing.T) {
   686  			source := makeProjection("", utilptr.Int32Ptr(tc.mode), "downwardAPI")
   687  			source.Sources[0].DownwardAPI.Items = tc.volumeFile
   688  
   689  			client := fake.NewSimpleClientset(tc.pod)
   690  			tempDir, host := newTestHost(t, client)
   691  			defer os.RemoveAll(tempDir)
   692  			var myVolumeMounter = projectedVolumeMounter{
   693  				projectedVolume: &projectedVolume{
   694  					sources: source.Sources,
   695  					podUID:  tc.pod.UID,
   696  					plugin: &projectedPlugin{
   697  						host: host,
   698  					},
   699  				},
   700  				source: *source,
   701  				pod:    tc.pod,
   702  			}
   703  
   704  			actualPayload, err := myVolumeMounter.collectData(volume.MounterArgs{})
   705  			if err != nil && tc.success {
   706  				t.Errorf("%v: unexpected failure making payload: %v", tc.name, err)
   707  				return
   708  			}
   709  			if err == nil && !tc.success {
   710  				t.Errorf("%v: unexpected success making payload", tc.name)
   711  				return
   712  			}
   713  			if !tc.success {
   714  				return
   715  			}
   716  			if e, a := tc.payload, actualPayload; !reflect.DeepEqual(e, a) {
   717  				t.Errorf("%v: expected and actual payload do not match", tc.name)
   718  			}
   719  		})
   720  
   721  	}
   722  }
   723  
   724  func TestCollectDataWithServiceAccountToken(t *testing.T) {
   725  	scheme := runtime.NewScheme()
   726  	utilruntime.Must(pkgauthenticationv1.RegisterDefaults(scheme))
   727  	utilruntime.Must(pkgcorev1.RegisterDefaults(scheme))
   728  
   729  	minute := int64(60)
   730  	cases := []struct {
   731  		name        string
   732  		svcacct     string
   733  		audience    string
   734  		defaultMode *int32
   735  		fsUser      *int64
   736  		fsGroup     *int64
   737  		expiration  *int64
   738  		path        string
   739  
   740  		wantPayload map[string]util.FileProjection
   741  		wantErr     error
   742  	}{
   743  		{
   744  			name:        "good service account",
   745  			audience:    "https://example.com",
   746  			defaultMode: utilptr.Int32Ptr(0644),
   747  			path:        "token",
   748  			expiration:  &minute,
   749  
   750  			wantPayload: map[string]util.FileProjection{
   751  				"token": {Data: []byte("test_projected_namespace:foo:60:[https://example.com]"), Mode: 0644},
   752  			},
   753  		},
   754  		{
   755  			name:        "good service account other path",
   756  			audience:    "https://example.com",
   757  			defaultMode: utilptr.Int32Ptr(0644),
   758  			path:        "other-token",
   759  			expiration:  &minute,
   760  			wantPayload: map[string]util.FileProjection{
   761  				"other-token": {Data: []byte("test_projected_namespace:foo:60:[https://example.com]"), Mode: 0644},
   762  			},
   763  		},
   764  		{
   765  			name:        "good service account defaults audience",
   766  			defaultMode: utilptr.Int32Ptr(0644),
   767  			path:        "token",
   768  			expiration:  &minute,
   769  
   770  			wantPayload: map[string]util.FileProjection{
   771  				"token": {Data: []byte("test_projected_namespace:foo:60:[https://api]"), Mode: 0644},
   772  			},
   773  		},
   774  		{
   775  			name:        "good service account defaults expiration",
   776  			defaultMode: utilptr.Int32Ptr(0644),
   777  			path:        "token",
   778  
   779  			wantPayload: map[string]util.FileProjection{
   780  				"token": {Data: []byte("test_projected_namespace:foo:3600:[https://api]"), Mode: 0644},
   781  			},
   782  		},
   783  		{
   784  			name:    "no default mode",
   785  			path:    "token",
   786  			wantErr: fmt.Errorf("no defaultMode used, not even the default value for it"),
   787  		},
   788  		{
   789  			name:        "fsUser != nil",
   790  			defaultMode: utilptr.Int32Ptr(0644),
   791  			fsUser:      utilptr.Int64Ptr(1000),
   792  			path:        "token",
   793  			wantPayload: map[string]util.FileProjection{
   794  				"token": {
   795  					Data:   []byte("test_projected_namespace:foo:3600:[https://api]"),
   796  					Mode:   0600,
   797  					FsUser: utilptr.Int64Ptr(1000),
   798  				},
   799  			},
   800  		},
   801  		{
   802  			name:        "fsGroup != nil",
   803  			defaultMode: utilptr.Int32Ptr(0644),
   804  			fsGroup:     utilptr.Int64Ptr(1000),
   805  			path:        "token",
   806  			wantPayload: map[string]util.FileProjection{
   807  				"token": {
   808  					Data: []byte("test_projected_namespace:foo:3600:[https://api]"),
   809  					Mode: 0600,
   810  				},
   811  			},
   812  		},
   813  		{
   814  			name:        "fsUser != nil && fsGroup != nil",
   815  			defaultMode: utilptr.Int32Ptr(0644),
   816  			fsGroup:     utilptr.Int64Ptr(1000),
   817  			fsUser:      utilptr.Int64Ptr(1000),
   818  			path:        "token",
   819  			wantPayload: map[string]util.FileProjection{
   820  				"token": {
   821  					Data:   []byte("test_projected_namespace:foo:3600:[https://api]"),
   822  					Mode:   0600,
   823  					FsUser: utilptr.Int64Ptr(1000),
   824  				},
   825  			},
   826  		},
   827  	}
   828  
   829  	for _, tc := range cases {
   830  		t.Run(tc.name, func(t *testing.T) {
   831  			testNamespace := "test_projected_namespace"
   832  			source := makeProjection(tc.name, tc.defaultMode, "serviceAccountToken")
   833  			source.Sources[0].ServiceAccountToken.Audience = tc.audience
   834  			source.Sources[0].ServiceAccountToken.ExpirationSeconds = tc.expiration
   835  			source.Sources[0].ServiceAccountToken.Path = tc.path
   836  
   837  			testPodUID := types.UID("test_pod_uid")
   838  			pod := &v1.Pod{
   839  				ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID},
   840  				Spec:       v1.PodSpec{ServiceAccountName: "foo"},
   841  			}
   842  			scheme.Default(pod)
   843  
   844  			client := &fake.Clientset{}
   845  			client.AddReactor("create", "serviceaccounts", clitesting.ReactionFunc(func(action clitesting.Action) (bool, runtime.Object, error) {
   846  				tr := action.(clitesting.CreateAction).GetObject().(*authenticationv1.TokenRequest)
   847  				scheme.Default(tr)
   848  				if len(tr.Spec.Audiences) == 0 {
   849  					tr.Spec.Audiences = []string{"https://api"}
   850  				}
   851  				tr.Status.Token = fmt.Sprintf("%v:%v:%d:%v", action.GetNamespace(), "foo", *tr.Spec.ExpirationSeconds, tr.Spec.Audiences)
   852  				return true, tr, nil
   853  			}))
   854  
   855  			tempDir, host := newTestHost(t, client)
   856  			defer os.RemoveAll(tempDir)
   857  
   858  			var myVolumeMounter = projectedVolumeMounter{
   859  				projectedVolume: &projectedVolume{
   860  					sources: source.Sources,
   861  					podUID:  pod.UID,
   862  					plugin: &projectedPlugin{
   863  						host:                   host,
   864  						getServiceAccountToken: host.GetServiceAccountTokenFunc(),
   865  					},
   866  				},
   867  				source: *source,
   868  				pod:    pod,
   869  			}
   870  
   871  			gotPayload, err := myVolumeMounter.collectData(volume.MounterArgs{FsUser: tc.fsUser, FsGroup: tc.fsGroup})
   872  			if err != nil && (tc.wantErr == nil || tc.wantErr.Error() != err.Error()) {
   873  				t.Fatalf("collectData() = unexpected err: %v", err)
   874  			}
   875  			if diff := cmp.Diff(tc.wantPayload, gotPayload); diff != "" {
   876  				t.Errorf("collectData() = unexpected diff (-want +got):\n%s", diff)
   877  			}
   878  		})
   879  	}
   880  }
   881  
   882  func TestCollectDataWithClusterTrustBundle(t *testing.T) {
   883  	// This test is limited by the use of a fake clientset and volume host.  We
   884  	// can't meaningfully test that label selectors end up doing the correct
   885  	// thing for example.
   886  
   887  	goodCert1 := mustMakeRoot(t, "root1")
   888  
   889  	testCases := []struct {
   890  		name string
   891  
   892  		source  v1.ProjectedVolumeSource
   893  		bundles []runtime.Object
   894  
   895  		fsUser  *int64
   896  		fsGroup *int64
   897  
   898  		wantPayload map[string]util.FileProjection
   899  		wantErr     error
   900  	}{
   901  		{
   902  			name: "single ClusterTrustBundle by name",
   903  			source: v1.ProjectedVolumeSource{
   904  				Sources: []v1.VolumeProjection{
   905  					{
   906  						ClusterTrustBundle: &v1.ClusterTrustBundleProjection{
   907  							Name: utilptr.String("foo"),
   908  							Path: "bundle.pem",
   909  						},
   910  					},
   911  				},
   912  				DefaultMode: utilptr.Int32(0644),
   913  			},
   914  			bundles: []runtime.Object{
   915  				&certificatesv1alpha1.ClusterTrustBundle{
   916  					ObjectMeta: metav1.ObjectMeta{
   917  						Name: "foo",
   918  					},
   919  					Spec: certificatesv1alpha1.ClusterTrustBundleSpec{
   920  						TrustBundle: string(goodCert1),
   921  					},
   922  				},
   923  			},
   924  			wantPayload: map[string]util.FileProjection{
   925  				"bundle.pem": {
   926  					Data: []byte(goodCert1),
   927  					Mode: 0644,
   928  				},
   929  			},
   930  		},
   931  		{
   932  			name: "single ClusterTrustBundle by signer name",
   933  			source: v1.ProjectedVolumeSource{
   934  				Sources: []v1.VolumeProjection{
   935  					{
   936  						ClusterTrustBundle: &v1.ClusterTrustBundleProjection{
   937  							SignerName: utilptr.String("foo.example/bar"), // Note: fake client doesn't understand selection by signer name.
   938  							LabelSelector: &metav1.LabelSelector{
   939  								MatchLabels: map[string]string{
   940  									"key": "non-value", // Note: fake client doesn't actually act on label selectors.
   941  								},
   942  							},
   943  							Path: "bundle.pem",
   944  						},
   945  					},
   946  				},
   947  				DefaultMode: utilptr.Int32(0644),
   948  			},
   949  			bundles: []runtime.Object{
   950  				&certificatesv1alpha1.ClusterTrustBundle{
   951  					ObjectMeta: metav1.ObjectMeta{
   952  						Name: "foo:example:bar",
   953  						Labels: map[string]string{
   954  							"key": "value",
   955  						},
   956  					},
   957  					Spec: certificatesv1alpha1.ClusterTrustBundleSpec{
   958  						SignerName:  "foo.example/bar",
   959  						TrustBundle: string(goodCert1),
   960  					},
   961  				},
   962  			},
   963  			wantPayload: map[string]util.FileProjection{
   964  				"bundle.pem": {
   965  					Data: []byte(goodCert1),
   966  					Mode: 0644,
   967  				},
   968  			},
   969  		},
   970  		{
   971  			name: "single ClusterTrustBundle by name, non-default mode",
   972  			source: v1.ProjectedVolumeSource{
   973  				Sources: []v1.VolumeProjection{
   974  					{
   975  						ClusterTrustBundle: &v1.ClusterTrustBundleProjection{
   976  							Name: utilptr.String("foo"),
   977  							Path: "bundle.pem",
   978  						},
   979  					},
   980  				},
   981  				DefaultMode: utilptr.Int32(0600),
   982  			},
   983  			bundles: []runtime.Object{
   984  				&certificatesv1alpha1.ClusterTrustBundle{
   985  					ObjectMeta: metav1.ObjectMeta{
   986  						Name: "foo",
   987  					},
   988  					Spec: certificatesv1alpha1.ClusterTrustBundleSpec{
   989  						TrustBundle: string(goodCert1),
   990  					},
   991  				},
   992  			},
   993  			wantPayload: map[string]util.FileProjection{
   994  				"bundle.pem": {
   995  					Data: []byte(goodCert1),
   996  					Mode: 0600,
   997  				},
   998  			},
   999  		},
  1000  	}
  1001  
  1002  	for _, tc := range testCases {
  1003  		t.Run(tc.name, func(t *testing.T) {
  1004  			pod := &v1.Pod{
  1005  				ObjectMeta: metav1.ObjectMeta{
  1006  					Namespace: "default",
  1007  					UID:       types.UID("test_pod_uid"),
  1008  				},
  1009  				Spec: v1.PodSpec{ServiceAccountName: "foo"},
  1010  			}
  1011  
  1012  			client := fake.NewSimpleClientset(tc.bundles...)
  1013  
  1014  			tempDir, host := newTestHost(t, client)
  1015  			defer os.RemoveAll(tempDir)
  1016  
  1017  			var myVolumeMounter = projectedVolumeMounter{
  1018  				projectedVolume: &projectedVolume{
  1019  					sources: tc.source.Sources,
  1020  					podUID:  pod.UID,
  1021  					plugin: &projectedPlugin{
  1022  						host:   host,
  1023  						kvHost: host.(volume.KubeletVolumeHost),
  1024  					},
  1025  				},
  1026  				source: tc.source,
  1027  				pod:    pod,
  1028  			}
  1029  
  1030  			gotPayload, err := myVolumeMounter.collectData(volume.MounterArgs{FsUser: tc.fsUser, FsGroup: tc.fsGroup})
  1031  			if err != nil {
  1032  				t.Fatalf("Unexpected failure making payload: %v", err)
  1033  			}
  1034  			if diff := cmp.Diff(tc.wantPayload, gotPayload); diff != "" {
  1035  				t.Fatalf("Bad payload; diff (-want +got)\n%s", diff)
  1036  			}
  1037  		})
  1038  	}
  1039  }
  1040  
  1041  func newTestHost(t *testing.T, clientset clientset.Interface) (string, volume.VolumeHost) {
  1042  	tempDir, err := os.MkdirTemp("", "projected_volume_test.")
  1043  	if err != nil {
  1044  		t.Fatalf("can't make a temp rootdir: %v", err)
  1045  	}
  1046  
  1047  	return tempDir, volumetest.NewFakeKubeletVolumeHost(t, tempDir, clientset, emptydir.ProbeVolumePlugins())
  1048  }
  1049  
  1050  func TestCanSupport(t *testing.T) {
  1051  	pluginMgr := volume.VolumePluginMgr{}
  1052  	tempDir, host := newTestHost(t, nil)
  1053  	defer os.RemoveAll(tempDir)
  1054  	pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
  1055  
  1056  	plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
  1057  	if err != nil {
  1058  		t.Fatal("Can't find the plugin by name")
  1059  	}
  1060  	if plugin.GetPluginName() != projectedPluginName {
  1061  		t.Errorf("Wrong name: %s", plugin.GetPluginName())
  1062  	}
  1063  	if !plugin.CanSupport(&volume.Spec{Volume: &v1.Volume{VolumeSource: v1.VolumeSource{Projected: &v1.ProjectedVolumeSource{}}}}) {
  1064  		t.Errorf("Expected true")
  1065  	}
  1066  	if plugin.CanSupport(&volume.Spec{}) {
  1067  		t.Errorf("Expected false")
  1068  	}
  1069  }
  1070  
  1071  func TestPlugin(t *testing.T) {
  1072  	var (
  1073  		testPodUID     = types.UID("test_pod_uid")
  1074  		testVolumeName = "test_volume_name"
  1075  		testNamespace  = "test_projected_namespace"
  1076  		testName       = "test_projected_name"
  1077  
  1078  		volumeSpec    = makeVolumeSpec(testVolumeName, testName, 0644)
  1079  		secret        = makeSecret(testNamespace, testName)
  1080  		client        = fake.NewSimpleClientset(&secret)
  1081  		pluginMgr     = volume.VolumePluginMgr{}
  1082  		rootDir, host = newTestHost(t, client)
  1083  	)
  1084  	defer os.RemoveAll(rootDir)
  1085  	pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
  1086  
  1087  	plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
  1088  	if err != nil {
  1089  		t.Fatal("Can't find the plugin by name")
  1090  	}
  1091  
  1092  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
  1093  	mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
  1094  	if err != nil {
  1095  		t.Errorf("Failed to make a new Mounter: %v", err)
  1096  	}
  1097  	if mounter == nil {
  1098  		t.Errorf("Got a nil Mounter")
  1099  	}
  1100  
  1101  	volumePath := mounter.GetPath()
  1102  	if !strings.HasSuffix(volumePath, filepath.Join("pods/test_pod_uid/volumes/kubernetes.io~projected", testVolumeName)) {
  1103  		t.Errorf("Got unexpected path: %s", volumePath)
  1104  	}
  1105  
  1106  	err = mounter.SetUp(volume.MounterArgs{})
  1107  	if err != nil {
  1108  		t.Errorf("Failed to setup volume: %v", err)
  1109  	}
  1110  	if _, err := os.Stat(volumePath); err != nil {
  1111  		if os.IsNotExist(err) {
  1112  			t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
  1113  		} else {
  1114  			t.Errorf("SetUp() failed: %v", err)
  1115  		}
  1116  	}
  1117  
  1118  	// secret volume should create its own empty wrapper path
  1119  	podWrapperMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid/plugins/kubernetes.io~empty-dir/wrapped_test_volume_name", rootDir)
  1120  
  1121  	if _, err := os.Stat(podWrapperMetadataDir); err != nil {
  1122  		if os.IsNotExist(err) {
  1123  			t.Errorf("SetUp() failed, empty-dir wrapper path is not created: %s", podWrapperMetadataDir)
  1124  		} else {
  1125  			t.Errorf("SetUp() failed: %v", err)
  1126  		}
  1127  	}
  1128  	doTestSecretDataInVolume(volumePath, secret, t)
  1129  	defer doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
  1130  }
  1131  
  1132  func TestInvalidPathProjected(t *testing.T) {
  1133  	var (
  1134  		testPodUID     = types.UID("test_pod_uid")
  1135  		testVolumeName = "test_volume_name"
  1136  		testNamespace  = "test_projected_namespace"
  1137  		testName       = "test_projected_name"
  1138  
  1139  		volumeSpec    = makeVolumeSpec(testVolumeName, testName, 0644)
  1140  		secret        = makeSecret(testNamespace, testName)
  1141  		client        = fake.NewSimpleClientset(&secret)
  1142  		pluginMgr     = volume.VolumePluginMgr{}
  1143  		rootDir, host = newTestHost(t, client)
  1144  	)
  1145  	volumeSpec.Projected.Sources[0].Secret.Items = []v1.KeyToPath{
  1146  		{Key: "missing", Path: "missing"},
  1147  	}
  1148  
  1149  	defer os.RemoveAll(rootDir)
  1150  	pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
  1151  
  1152  	plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
  1153  	if err != nil {
  1154  		t.Fatal("Can't find the plugin by name")
  1155  	}
  1156  
  1157  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
  1158  	mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
  1159  	if err != nil {
  1160  		t.Errorf("Failed to make a new Mounter: %v", err)
  1161  	}
  1162  	if mounter == nil {
  1163  		t.Errorf("Got a nil Mounter")
  1164  	}
  1165  
  1166  	volumePath := mounter.GetPath()
  1167  	if !strings.HasSuffix(volumePath, filepath.Join("pods/test_pod_uid/volumes/kubernetes.io~projected", testVolumeName)) {
  1168  		t.Errorf("Got unexpected path: %s", volumePath)
  1169  	}
  1170  
  1171  	var mounterArgs volume.MounterArgs
  1172  	err = mounter.SetUp(mounterArgs)
  1173  	if err == nil {
  1174  		t.Errorf("Expected error while setting up secret")
  1175  	}
  1176  
  1177  	_, err = os.Stat(volumePath)
  1178  	if err == nil {
  1179  		t.Errorf("Expected path %s to not exist", volumePath)
  1180  	}
  1181  }
  1182  
  1183  // Test the case where the plugin's ready file exists, but the volume dir is not a
  1184  // mountpoint, which is the state the system will be in after reboot.  The dir
  1185  // should be mounter and the secret data written to it.
  1186  func TestPluginReboot(t *testing.T) {
  1187  	var (
  1188  		testPodUID     = types.UID("test_pod_uid3")
  1189  		testVolumeName = "test_volume_name"
  1190  		testNamespace  = "test_secret_namespace"
  1191  		testName       = "test_secret_name"
  1192  
  1193  		volumeSpec    = makeVolumeSpec(testVolumeName, testName, 0644)
  1194  		secret        = makeSecret(testNamespace, testName)
  1195  		client        = fake.NewSimpleClientset(&secret)
  1196  		pluginMgr     = volume.VolumePluginMgr{}
  1197  		rootDir, host = newTestHost(t, client)
  1198  	)
  1199  	defer os.RemoveAll(rootDir)
  1200  	pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
  1201  
  1202  	plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
  1203  	if err != nil {
  1204  		t.Fatal("Can't find the plugin by name")
  1205  	}
  1206  
  1207  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
  1208  	mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
  1209  	if err != nil {
  1210  		t.Errorf("Failed to make a new Mounter: %v", err)
  1211  	}
  1212  	if mounter == nil {
  1213  		t.Errorf("Got a nil Mounter")
  1214  	}
  1215  
  1216  	podMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid3/plugins/kubernetes.io~projected/test_volume_name", rootDir)
  1217  	util.SetReady(podMetadataDir)
  1218  	volumePath := mounter.GetPath()
  1219  	if !strings.HasSuffix(volumePath, filepath.FromSlash("pods/test_pod_uid3/volumes/kubernetes.io~projected/test_volume_name")) {
  1220  		t.Errorf("Got unexpected path: %s", volumePath)
  1221  	}
  1222  
  1223  	err = mounter.SetUp(volume.MounterArgs{})
  1224  	if err != nil {
  1225  		t.Errorf("Failed to setup volume: %v", err)
  1226  	}
  1227  	if _, err := os.Stat(volumePath); err != nil {
  1228  		if os.IsNotExist(err) {
  1229  			t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
  1230  		} else {
  1231  			t.Errorf("SetUp() failed: %v", err)
  1232  		}
  1233  	}
  1234  
  1235  	doTestSecretDataInVolume(volumePath, secret, t)
  1236  	doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
  1237  }
  1238  
  1239  func TestPluginOptional(t *testing.T) {
  1240  	var (
  1241  		testPodUID     = types.UID("test_pod_uid")
  1242  		testVolumeName = "test_volume_name"
  1243  		testNamespace  = "test_secret_namespace"
  1244  		testName       = "test_secret_name"
  1245  		trueVal        = true
  1246  
  1247  		volumeSpec    = makeVolumeSpec(testVolumeName, testName, 0644)
  1248  		client        = fake.NewSimpleClientset()
  1249  		pluginMgr     = volume.VolumePluginMgr{}
  1250  		rootDir, host = newTestHost(t, client)
  1251  	)
  1252  	volumeSpec.VolumeSource.Projected.Sources[0].Secret.Optional = &trueVal
  1253  	defer os.RemoveAll(rootDir)
  1254  	pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
  1255  
  1256  	plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
  1257  	if err != nil {
  1258  		t.Fatal("Can't find the plugin by name")
  1259  	}
  1260  
  1261  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
  1262  	mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
  1263  	if err != nil {
  1264  		t.Errorf("Failed to make a new Mounter: %v", err)
  1265  	}
  1266  	if mounter == nil {
  1267  		t.Errorf("Got a nil Mounter")
  1268  	}
  1269  
  1270  	volumePath := mounter.GetPath()
  1271  	if !strings.HasSuffix(volumePath, filepath.FromSlash("pods/test_pod_uid/volumes/kubernetes.io~projected/test_volume_name")) {
  1272  		t.Errorf("Got unexpected path: %s", volumePath)
  1273  	}
  1274  
  1275  	err = mounter.SetUp(volume.MounterArgs{})
  1276  	if err != nil {
  1277  		t.Errorf("Failed to setup volume: %v", err)
  1278  	}
  1279  	if _, err := os.Stat(volumePath); err != nil {
  1280  		if os.IsNotExist(err) {
  1281  			t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
  1282  		} else {
  1283  			t.Errorf("SetUp() failed: %v", err)
  1284  		}
  1285  	}
  1286  
  1287  	// secret volume should create its own empty wrapper path
  1288  	podWrapperMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid/plugins/kubernetes.io~empty-dir/wrapped_test_volume_name", rootDir)
  1289  
  1290  	if _, err := os.Stat(podWrapperMetadataDir); err != nil {
  1291  		if os.IsNotExist(err) {
  1292  			t.Errorf("SetUp() failed, empty-dir wrapper path is not created: %s", podWrapperMetadataDir)
  1293  		} else {
  1294  			t.Errorf("SetUp() failed: %v", err)
  1295  		}
  1296  	}
  1297  
  1298  	datadirSymlink := filepath.Join(volumePath, "..data")
  1299  	datadir, err := os.Readlink(datadirSymlink)
  1300  	if err != nil && os.IsNotExist(err) {
  1301  		t.Fatalf("couldn't find volume path's data dir, %s", datadirSymlink)
  1302  	} else if err != nil {
  1303  		t.Fatalf("couldn't read symlink, %s", datadirSymlink)
  1304  	}
  1305  	datadirPath := filepath.Join(volumePath, datadir)
  1306  
  1307  	infos, err := os.ReadDir(volumePath)
  1308  	if err != nil {
  1309  		t.Fatalf("couldn't find volume path, %s", volumePath)
  1310  	}
  1311  	if len(infos) != 0 {
  1312  		for _, fi := range infos {
  1313  			if fi.Name() != "..data" && fi.Name() != datadir {
  1314  				t.Errorf("empty data volume directory, %s, is not empty. Contains: %s", datadirSymlink, fi.Name())
  1315  			}
  1316  		}
  1317  	}
  1318  
  1319  	infos, err = os.ReadDir(datadirPath)
  1320  	if err != nil {
  1321  		t.Fatalf("couldn't find volume data path, %s", datadirPath)
  1322  	}
  1323  	if len(infos) != 0 {
  1324  		t.Errorf("empty data directory, %s, is not empty. Contains: %s", datadirSymlink, infos[0].Name())
  1325  	}
  1326  
  1327  	defer doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
  1328  }
  1329  
  1330  func TestPluginOptionalKeys(t *testing.T) {
  1331  	var (
  1332  		testPodUID     = types.UID("test_pod_uid")
  1333  		testVolumeName = "test_volume_name"
  1334  		testNamespace  = "test_secret_namespace"
  1335  		testName       = "test_secret_name"
  1336  		trueVal        = true
  1337  
  1338  		volumeSpec    = makeVolumeSpec(testVolumeName, testName, 0644)
  1339  		secret        = makeSecret(testNamespace, testName)
  1340  		client        = fake.NewSimpleClientset(&secret)
  1341  		pluginMgr     = volume.VolumePluginMgr{}
  1342  		rootDir, host = newTestHost(t, client)
  1343  	)
  1344  	volumeSpec.VolumeSource.Projected.Sources[0].Secret.Items = []v1.KeyToPath{
  1345  		{Key: "data-1", Path: "data-1"},
  1346  		{Key: "data-2", Path: "data-2"},
  1347  		{Key: "data-3", Path: "data-3"},
  1348  		{Key: "missing", Path: "missing"},
  1349  	}
  1350  	volumeSpec.VolumeSource.Projected.Sources[0].Secret.Optional = &trueVal
  1351  	defer os.RemoveAll(rootDir)
  1352  	pluginMgr.InitPlugins(ProbeVolumePlugins(), nil /* prober */, host)
  1353  
  1354  	plugin, err := pluginMgr.FindPluginByName(projectedPluginName)
  1355  	if err != nil {
  1356  		t.Fatal("Can't find the plugin by name")
  1357  	}
  1358  
  1359  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: testNamespace, UID: testPodUID}}
  1360  	mounter, err := plugin.NewMounter(volume.NewSpecFromVolume(volumeSpec), pod, volume.VolumeOptions{})
  1361  	if err != nil {
  1362  		t.Errorf("Failed to make a new Mounter: %v", err)
  1363  	}
  1364  	if mounter == nil {
  1365  		t.Errorf("Got a nil Mounter")
  1366  	}
  1367  
  1368  	volumePath := mounter.GetPath()
  1369  	if !strings.HasSuffix(volumePath, filepath.FromSlash("pods/test_pod_uid/volumes/kubernetes.io~projected/test_volume_name")) {
  1370  		t.Errorf("Got unexpected path: %s", volumePath)
  1371  	}
  1372  
  1373  	err = mounter.SetUp(volume.MounterArgs{})
  1374  	if err != nil {
  1375  		t.Errorf("Failed to setup volume: %v", err)
  1376  	}
  1377  	if _, err := os.Stat(volumePath); err != nil {
  1378  		if os.IsNotExist(err) {
  1379  			t.Errorf("SetUp() failed, volume path not created: %s", volumePath)
  1380  		} else {
  1381  			t.Errorf("SetUp() failed: %v", err)
  1382  		}
  1383  	}
  1384  
  1385  	// secret volume should create its own empty wrapper path
  1386  	podWrapperMetadataDir := fmt.Sprintf("%v/pods/test_pod_uid/plugins/kubernetes.io~empty-dir/wrapped_test_volume_name", rootDir)
  1387  
  1388  	if _, err := os.Stat(podWrapperMetadataDir); err != nil {
  1389  		if os.IsNotExist(err) {
  1390  			t.Errorf("SetUp() failed, empty-dir wrapper path is not created: %s", podWrapperMetadataDir)
  1391  		} else {
  1392  			t.Errorf("SetUp() failed: %v", err)
  1393  		}
  1394  	}
  1395  	doTestSecretDataInVolume(volumePath, secret, t)
  1396  	defer doTestCleanAndTeardown(plugin, testPodUID, testVolumeName, volumePath, t)
  1397  }
  1398  
  1399  func makeVolumeSpec(volumeName, name string, defaultMode int32) *v1.Volume {
  1400  	return &v1.Volume{
  1401  		Name: volumeName,
  1402  		VolumeSource: v1.VolumeSource{
  1403  			Projected: makeProjection(name, utilptr.Int32Ptr(defaultMode), "secret"),
  1404  		},
  1405  	}
  1406  }
  1407  
  1408  func makeSecret(namespace, name string) v1.Secret {
  1409  	return v1.Secret{
  1410  		ObjectMeta: metav1.ObjectMeta{
  1411  			Namespace: namespace,
  1412  			Name:      name,
  1413  		},
  1414  		Data: map[string][]byte{
  1415  			"data-1": []byte("value-1"),
  1416  			"data-2": []byte("value-2"),
  1417  			"data-3": []byte("value-3"),
  1418  		},
  1419  	}
  1420  }
  1421  
  1422  func makeProjection(name string, defaultMode *int32, kind string) *v1.ProjectedVolumeSource {
  1423  	var item v1.VolumeProjection
  1424  
  1425  	switch kind {
  1426  	case "configMap":
  1427  		item = v1.VolumeProjection{
  1428  			ConfigMap: &v1.ConfigMapProjection{
  1429  				LocalObjectReference: v1.LocalObjectReference{Name: name},
  1430  			},
  1431  		}
  1432  	case "secret":
  1433  		item = v1.VolumeProjection{
  1434  			Secret: &v1.SecretProjection{
  1435  				LocalObjectReference: v1.LocalObjectReference{Name: name},
  1436  			},
  1437  		}
  1438  	case "downwardAPI":
  1439  		item = v1.VolumeProjection{
  1440  			DownwardAPI: &v1.DownwardAPIProjection{},
  1441  		}
  1442  	case "serviceAccountToken":
  1443  		item = v1.VolumeProjection{
  1444  			ServiceAccountToken: &v1.ServiceAccountTokenProjection{},
  1445  		}
  1446  	}
  1447  
  1448  	return &v1.ProjectedVolumeSource{
  1449  		Sources:     []v1.VolumeProjection{item},
  1450  		DefaultMode: defaultMode,
  1451  	}
  1452  }
  1453  
  1454  func doTestSecretDataInVolume(volumePath string, secret v1.Secret, t *testing.T) {
  1455  	for key, value := range secret.Data {
  1456  		secretDataHostPath := filepath.Join(volumePath, key)
  1457  		if _, err := os.Stat(secretDataHostPath); err != nil {
  1458  			t.Fatalf("SetUp() failed, couldn't find secret data on disk: %v", secretDataHostPath)
  1459  		} else {
  1460  			actualSecretBytes, err := os.ReadFile(secretDataHostPath)
  1461  			if err != nil {
  1462  				t.Fatalf("Couldn't read secret data from: %v", secretDataHostPath)
  1463  			}
  1464  
  1465  			actualSecretValue := string(actualSecretBytes)
  1466  			if string(value) != actualSecretValue {
  1467  				t.Errorf("Unexpected value; expected %q, got %q", value, actualSecretValue)
  1468  			}
  1469  		}
  1470  	}
  1471  }
  1472  
  1473  func doTestCleanAndTeardown(plugin volume.VolumePlugin, podUID types.UID, testVolumeName, volumePath string, t *testing.T) {
  1474  	unmounter, err := plugin.NewUnmounter(testVolumeName, podUID)
  1475  	if err != nil {
  1476  		t.Errorf("Failed to make a new Unmounter: %v", err)
  1477  	}
  1478  	if unmounter == nil {
  1479  		t.Errorf("Got a nil Unmounter")
  1480  	}
  1481  
  1482  	if err := unmounter.TearDown(); err != nil {
  1483  		t.Errorf("Expected success, got: %v", err)
  1484  	}
  1485  	if _, err := os.Stat(volumePath); err == nil {
  1486  		t.Errorf("TearDown() failed, volume path still exists: %s", volumePath)
  1487  	} else if !os.IsNotExist(err) {
  1488  		t.Errorf("TearDown() failed: %v", err)
  1489  	}
  1490  }
  1491  
  1492  func mustMakeRoot(t *testing.T, cn string) string {
  1493  	pub, priv, err := ed25519.GenerateKey(rand.Reader)
  1494  	if err != nil {
  1495  		t.Fatalf("Error while generating key: %v", err)
  1496  	}
  1497  
  1498  	template := &x509.Certificate{
  1499  		SerialNumber: big.NewInt(0),
  1500  		Subject: pkix.Name{
  1501  			CommonName: cn,
  1502  		},
  1503  		IsCA:                  true,
  1504  		BasicConstraintsValid: true,
  1505  	}
  1506  
  1507  	cert, err := x509.CreateCertificate(rand.Reader, template, template, pub, priv)
  1508  	if err != nil {
  1509  		t.Fatalf("Error while making certificate: %v", err)
  1510  	}
  1511  
  1512  	return string(pem.EncodeToMemory(&pem.Block{
  1513  		Type:    "CERTIFICATE",
  1514  		Headers: nil,
  1515  		Bytes:   cert,
  1516  	}))
  1517  }
  1518  

View as plain text