mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Add PV util for extracting referenced secrets
This commit is contained in:
parent
2d2427b847
commit
48a9357926
@ -59,6 +59,7 @@ pkg/api/errors
|
|||||||
pkg/api/events
|
pkg/api/events
|
||||||
pkg/api/install
|
pkg/api/install
|
||||||
pkg/api/meta
|
pkg/api/meta
|
||||||
|
pkg/api/persistentvolume
|
||||||
pkg/api/pod
|
pkg/api/pod
|
||||||
pkg/api/resource
|
pkg/api/resource
|
||||||
pkg/api/service
|
pkg/api/service
|
||||||
|
@ -107,6 +107,7 @@ filegroup(
|
|||||||
"//pkg/api/helper:all-srcs",
|
"//pkg/api/helper:all-srcs",
|
||||||
"//pkg/api/install:all-srcs",
|
"//pkg/api/install:all-srcs",
|
||||||
"//pkg/api/meta:all-srcs",
|
"//pkg/api/meta:all-srcs",
|
||||||
|
"//pkg/api/persistentvolume:all-srcs",
|
||||||
"//pkg/api/pod:all-srcs",
|
"//pkg/api/pod:all-srcs",
|
||||||
"//pkg/api/ref:all-srcs",
|
"//pkg/api/ref:all-srcs",
|
||||||
"//pkg/api/resource:all-srcs",
|
"//pkg/api/resource:all-srcs",
|
||||||
|
41
pkg/api/persistentvolume/BUILD
Normal file
41
pkg/api/persistentvolume/BUILD
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
licenses(["notice"])
|
||||||
|
|
||||||
|
load(
|
||||||
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
|
"go_library",
|
||||||
|
"go_test",
|
||||||
|
)
|
||||||
|
|
||||||
|
go_library(
|
||||||
|
name = "go_default_library",
|
||||||
|
srcs = ["util.go"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = ["//pkg/api:go_default_library"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "package-srcs",
|
||||||
|
srcs = glob(["**"]),
|
||||||
|
tags = ["automanaged"],
|
||||||
|
visibility = ["//visibility:private"],
|
||||||
|
)
|
||||||
|
|
||||||
|
filegroup(
|
||||||
|
name = "all-srcs",
|
||||||
|
srcs = [":package-srcs"],
|
||||||
|
tags = ["automanaged"],
|
||||||
|
)
|
||||||
|
|
||||||
|
go_test(
|
||||||
|
name = "go_default_test",
|
||||||
|
srcs = ["util_test.go"],
|
||||||
|
library = ":go_default_library",
|
||||||
|
tags = ["automanaged"],
|
||||||
|
deps = [
|
||||||
|
"//pkg/api:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||||
|
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||||
|
],
|
||||||
|
)
|
4
pkg/api/persistentvolume/OWNERS
Executable file
4
pkg/api/persistentvolume/OWNERS
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
reviewers:
|
||||||
|
- smarterclayton
|
||||||
|
- kargakis
|
||||||
|
- david-mcmahon
|
55
pkg/api/persistentvolume/util.go
Normal file
55
pkg/api/persistentvolume/util.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package persistentvolume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VisitPVSecretNames invokes the visitor function with the name of every secret
|
||||||
|
// referenced by the PV spec. If visitor returns false, visiting is short-circuited.
|
||||||
|
// Returns true if visiting completed, false if visiting was short-circuited.
|
||||||
|
func VisitPVSecretNames(pv *api.PersistentVolume, visitor func(string) bool) bool {
|
||||||
|
source := &pv.Spec.PersistentVolumeSource
|
||||||
|
switch {
|
||||||
|
case source.AzureFile != nil:
|
||||||
|
if len(source.AzureFile.SecretName) > 0 && !visitor(source.AzureFile.SecretName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case source.CephFS != nil:
|
||||||
|
if source.CephFS.SecretRef != nil && !visitor(source.CephFS.SecretRef.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case source.FlexVolume != nil:
|
||||||
|
if source.FlexVolume.SecretRef != nil && !visitor(source.FlexVolume.SecretRef.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case source.RBD != nil:
|
||||||
|
if source.RBD.SecretRef != nil && !visitor(source.RBD.SecretRef.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case source.ScaleIO != nil:
|
||||||
|
if source.ScaleIO.SecretRef != nil && !visitor(source.ScaleIO.SecretRef.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
case source.ISCSI != nil:
|
||||||
|
if source.ISCSI.SecretRef != nil && !visitor(source.ISCSI.SecretRef.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
133
pkg/api/persistentvolume/util_test.go
Normal file
133
pkg/api/persistentvolume/util_test.go
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2017 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package persistentvolume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPVSecrets(t *testing.T) {
|
||||||
|
// Stub containing all possible secret references in a PV.
|
||||||
|
// The names of the referenced secrets match struct paths detected by reflection.
|
||||||
|
pvs := []*api.PersistentVolume{
|
||||||
|
{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
AzureFile: &api.AzureFileVolumeSource{
|
||||||
|
SecretName: "Spec.PersistentVolumeSource.AzureFile.SecretName"}}}},
|
||||||
|
{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
CephFS: &api.CephFSVolumeSource{
|
||||||
|
SecretRef: &api.LocalObjectReference{
|
||||||
|
Name: "Spec.PersistentVolumeSource.CephFS.SecretRef"}}}}},
|
||||||
|
{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
FlexVolume: &api.FlexVolumeSource{
|
||||||
|
SecretRef: &api.LocalObjectReference{
|
||||||
|
Name: "Spec.PersistentVolumeSource.FlexVolume.SecretRef"}}}}},
|
||||||
|
{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
RBD: &api.RBDVolumeSource{
|
||||||
|
SecretRef: &api.LocalObjectReference{
|
||||||
|
Name: "Spec.PersistentVolumeSource.RBD.SecretRef"}}}}},
|
||||||
|
{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
ScaleIO: &api.ScaleIOVolumeSource{
|
||||||
|
SecretRef: &api.LocalObjectReference{
|
||||||
|
Name: "Spec.PersistentVolumeSource.ScaleIO.SecretRef"}}}}},
|
||||||
|
{Spec: api.PersistentVolumeSpec{PersistentVolumeSource: api.PersistentVolumeSource{
|
||||||
|
ISCSI: &api.ISCSIVolumeSource{
|
||||||
|
SecretRef: &api.LocalObjectReference{
|
||||||
|
Name: "Spec.PersistentVolumeSource.ISCSI.SecretRef"}}}}},
|
||||||
|
}
|
||||||
|
extractedNames := sets.NewString()
|
||||||
|
for _, pv := range pvs {
|
||||||
|
VisitPVSecretNames(pv, func(name string) bool {
|
||||||
|
extractedNames.Insert(name)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// excludedSecretPaths holds struct paths to fields with "secret" in the name that are not actually references to secret API objects
|
||||||
|
excludedSecretPaths := sets.NewString(
|
||||||
|
"Spec.PersistentVolumeSource.CephFS.SecretFile",
|
||||||
|
)
|
||||||
|
// expectedSecretPaths holds struct paths to fields with "secret" in the name that are references to secret API objects.
|
||||||
|
// every path here should be represented as an example in the PV stub above, with the secret name set to the path.
|
||||||
|
expectedSecretPaths := sets.NewString(
|
||||||
|
"Spec.PersistentVolumeSource.AzureFile.SecretName",
|
||||||
|
"Spec.PersistentVolumeSource.CephFS.SecretRef",
|
||||||
|
"Spec.PersistentVolumeSource.FlexVolume.SecretRef",
|
||||||
|
"Spec.PersistentVolumeSource.RBD.SecretRef",
|
||||||
|
"Spec.PersistentVolumeSource.ScaleIO.SecretRef",
|
||||||
|
"Spec.PersistentVolumeSource.ISCSI.SecretRef",
|
||||||
|
)
|
||||||
|
secretPaths := collectSecretPaths(t, nil, "", reflect.TypeOf(&api.PersistentVolume{}))
|
||||||
|
secretPaths = secretPaths.Difference(excludedSecretPaths)
|
||||||
|
if missingPaths := expectedSecretPaths.Difference(secretPaths); len(missingPaths) > 0 {
|
||||||
|
t.Logf("Missing expected secret paths:\n%s", strings.Join(missingPaths.List(), "\n"))
|
||||||
|
t.Error("Missing expected secret paths. Verify VisitPVSecretNames() is correctly finding the missing paths, then correct expectedSecretPaths")
|
||||||
|
}
|
||||||
|
if extraPaths := secretPaths.Difference(expectedSecretPaths); len(extraPaths) > 0 {
|
||||||
|
t.Logf("Extra secret paths:\n%s", strings.Join(extraPaths.List(), "\n"))
|
||||||
|
t.Error("Extra fields with 'secret' in the name found. Verify VisitPVSecretNames() is including these fields if appropriate, then correct expectedSecretPaths")
|
||||||
|
}
|
||||||
|
|
||||||
|
if missingNames := expectedSecretPaths.Difference(extractedNames); len(missingNames) > 0 {
|
||||||
|
t.Logf("Missing expected secret names:\n%s", strings.Join(missingNames.List(), "\n"))
|
||||||
|
t.Error("Missing expected secret names. Verify the PV stub above includes these references, then verify VisitPVSecretNames() is correctly finding the missing names")
|
||||||
|
}
|
||||||
|
if extraNames := extractedNames.Difference(expectedSecretPaths); len(extraNames) > 0 {
|
||||||
|
t.Logf("Extra secret names:\n%s", strings.Join(extraNames.List(), "\n"))
|
||||||
|
t.Error("Extra secret names extracted. Verify VisitPVSecretNames() is correctly extracting secret names")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// collectSecretPaths traverses the object, computing all the struct paths that lead to fields with "secret" in the name.
|
||||||
|
func collectSecretPaths(t *testing.T, path *field.Path, name string, tp reflect.Type) sets.String {
|
||||||
|
secretPaths := sets.NewString()
|
||||||
|
|
||||||
|
if tp.Kind() == reflect.Ptr {
|
||||||
|
secretPaths.Insert(collectSecretPaths(t, path, name, tp.Elem()).List()...)
|
||||||
|
return secretPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(strings.ToLower(name), "secret") {
|
||||||
|
secretPaths.Insert(path.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch tp.Kind() {
|
||||||
|
case reflect.Ptr:
|
||||||
|
secretPaths.Insert(collectSecretPaths(t, path, name, tp.Elem()).List()...)
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < tp.NumField(); i++ {
|
||||||
|
field := tp.Field(i)
|
||||||
|
secretPaths.Insert(collectSecretPaths(t, path.Child(field.Name), field.Name, field.Type).List()...)
|
||||||
|
}
|
||||||
|
case reflect.Interface:
|
||||||
|
t.Errorf("cannot find secret fields in interface{} field %s", path.String())
|
||||||
|
case reflect.Map:
|
||||||
|
secretPaths.Insert(collectSecretPaths(t, path.Key("*"), "", tp.Elem()).List()...)
|
||||||
|
case reflect.Slice:
|
||||||
|
secretPaths.Insert(collectSecretPaths(t, path.Key("*"), "", tp.Elem()).List()...)
|
||||||
|
default:
|
||||||
|
// all primitive types
|
||||||
|
}
|
||||||
|
|
||||||
|
return secretPaths
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user