mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-08 14:21:18 +00:00
Internal ItemBlockAction plugins
This PR implements the internal ItemBlockAction plugins needed for pod, PVC, and SA. Signed-off-by: Scott Seago <sseago@redhat.com>
This commit is contained in:
@@ -26,8 +26,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/actionhelpers"
|
||||
)
|
||||
|
||||
// PVCAction inspects a PersistentVolumeClaim for the PersistentVolume
|
||||
@@ -51,7 +51,7 @@ func (a *PVCAction) AppliesTo() (velero.ResourceSelector, error) {
|
||||
func (a *PVCAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) {
|
||||
a.log.Info("Executing PVCAction")
|
||||
|
||||
var pvc corev1api.PersistentVolumeClaim
|
||||
pvc := new(corev1api.PersistentVolumeClaim)
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), &pvc); err != nil {
|
||||
return nil, nil, errors.Wrap(err, "unable to convert unstructured item to persistent volume claim")
|
||||
}
|
||||
@@ -60,10 +60,6 @@ func (a *PVCAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti
|
||||
return item, nil, nil
|
||||
}
|
||||
|
||||
pv := velero.ResourceIdentifier{
|
||||
GroupResource: kuberesource.PersistentVolumes,
|
||||
Name: pvc.Spec.VolumeName,
|
||||
}
|
||||
// remove dataSource if exists from prior restored CSI volumes
|
||||
if pvc.Spec.DataSource != nil {
|
||||
pvc.Spec.DataSource = nil
|
||||
@@ -94,5 +90,5 @@ func (a *PVCAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti
|
||||
return nil, nil, errors.Wrap(err, "unable to convert pvc to unstructured item")
|
||||
}
|
||||
|
||||
return &unstructured.Unstructured{Object: pvcMap}, []velero.ResourceIdentifier{pv}, nil
|
||||
return &unstructured.Unstructured{Object: pvcMap}, actionhelpers.RelatedItemsForPVC(pvc, a.log), nil
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/actionhelpers"
|
||||
)
|
||||
|
||||
// PodAction implements ItemAction.
|
||||
@@ -55,32 +55,5 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), pod); err != nil {
|
||||
return nil, nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
var additionalItems []velero.ResourceIdentifier
|
||||
if pod.Spec.PriorityClassName != "" {
|
||||
a.log.Infof("Adding priorityclass %s to additionalItems", pod.Spec.PriorityClassName)
|
||||
additionalItems = append(additionalItems, velero.ResourceIdentifier{
|
||||
GroupResource: kuberesource.PriorityClasses,
|
||||
Name: pod.Spec.PriorityClassName,
|
||||
})
|
||||
}
|
||||
|
||||
if len(pod.Spec.Volumes) == 0 {
|
||||
a.log.Info("pod has no volumes")
|
||||
return item, additionalItems, nil
|
||||
}
|
||||
|
||||
for _, volume := range pod.Spec.Volumes {
|
||||
if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.ClaimName != "" {
|
||||
a.log.Infof("Adding pvc %s to additionalItems", volume.PersistentVolumeClaim.ClaimName)
|
||||
|
||||
additionalItems = append(additionalItems, velero.ResourceIdentifier{
|
||||
GroupResource: kuberesource.PersistentVolumeClaims,
|
||||
Namespace: pod.Namespace,
|
||||
Name: volume.PersistentVolumeClaim.ClaimName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return item, additionalItems, nil
|
||||
return item, actionhelpers.RelatedItemsForPod(pod, a.log), nil
|
||||
}
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
Copyright 2018 the Velero contributors.
|
||||
|
||||
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 actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
rbacbeta "k8s.io/api/rbac/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
rbacclient "k8s.io/client-go/kubernetes/typed/rbac/v1"
|
||||
rbacbetaclient "k8s.io/client-go/kubernetes/typed/rbac/v1beta1"
|
||||
)
|
||||
|
||||
// ClusterRoleBindingLister allows for listing ClusterRoleBindings in a version-independent way.
|
||||
type ClusterRoleBindingLister interface {
|
||||
// List returns a slice of ClusterRoleBindings which can represent either v1 or v1beta1 ClusterRoleBindings.
|
||||
List() ([]ClusterRoleBinding, error)
|
||||
}
|
||||
|
||||
// noopClusterRoleBindingLister exists to handle clusters where RBAC is disabled.
|
||||
type noopClusterRoleBindingLister struct {
|
||||
}
|
||||
|
||||
func (noop noopClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) {
|
||||
return []ClusterRoleBinding{}, nil
|
||||
}
|
||||
|
||||
type v1ClusterRoleBindingLister struct {
|
||||
client rbacclient.ClusterRoleBindingInterface
|
||||
}
|
||||
|
||||
func (v1 v1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) {
|
||||
crbList, err := v1.client.List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
var crbs []ClusterRoleBinding
|
||||
for _, crb := range crbList.Items {
|
||||
crbs = append(crbs, v1ClusterRoleBinding{crb: crb})
|
||||
}
|
||||
|
||||
return crbs, nil
|
||||
}
|
||||
|
||||
type v1beta1ClusterRoleBindingLister struct {
|
||||
client rbacbetaclient.ClusterRoleBindingInterface
|
||||
}
|
||||
|
||||
func (v1beta1 v1beta1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) {
|
||||
crbList, err := v1beta1.client.List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
var crbs []ClusterRoleBinding
|
||||
for _, crb := range crbList.Items {
|
||||
crbs = append(crbs, v1beta1ClusterRoleBinding{crb: crb})
|
||||
}
|
||||
|
||||
return crbs, nil
|
||||
}
|
||||
|
||||
// NewClusterRoleBindingListerMap creates a map of RBAC version strings to their associated
|
||||
// ClusterRoleBindingLister structs.
|
||||
// Necessary so that callers to the ClusterRoleBindingLister interfaces don't need the kubernetes.Interface.
|
||||
func NewClusterRoleBindingListerMap(clientset kubernetes.Interface) map[string]ClusterRoleBindingLister {
|
||||
return map[string]ClusterRoleBindingLister{
|
||||
rbac.SchemeGroupVersion.Version: v1ClusterRoleBindingLister{client: clientset.RbacV1().ClusterRoleBindings()},
|
||||
rbacbeta.SchemeGroupVersion.Version: v1beta1ClusterRoleBindingLister{client: clientset.RbacV1beta1().ClusterRoleBindings()},
|
||||
"": noopClusterRoleBindingLister{},
|
||||
}
|
||||
}
|
||||
|
||||
// ClusterRoleBinding abstracts access to ClusterRoleBindings whether they're v1 or v1beta1.
|
||||
type ClusterRoleBinding interface {
|
||||
// Name returns the name of a ClusterRoleBinding.
|
||||
Name() string
|
||||
// ServiceAccountSubjects returns the names of subjects that are service accounts in the given namespace.
|
||||
ServiceAccountSubjects(namespace string) []string
|
||||
// RoleRefName returns the name of a ClusterRoleBinding's RoleRef.
|
||||
RoleRefName() string
|
||||
}
|
||||
|
||||
type v1ClusterRoleBinding struct {
|
||||
crb rbac.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (c v1ClusterRoleBinding) Name() string {
|
||||
return c.crb.Name
|
||||
}
|
||||
|
||||
func (c v1ClusterRoleBinding) RoleRefName() string {
|
||||
return c.crb.RoleRef.Name
|
||||
}
|
||||
|
||||
func (c v1ClusterRoleBinding) ServiceAccountSubjects(namespace string) []string {
|
||||
var saSubjects []string
|
||||
for _, s := range c.crb.Subjects {
|
||||
if s.Kind == rbac.ServiceAccountKind && s.Namespace == namespace {
|
||||
saSubjects = append(saSubjects, s.Name)
|
||||
}
|
||||
}
|
||||
return saSubjects
|
||||
}
|
||||
|
||||
type v1beta1ClusterRoleBinding struct {
|
||||
crb rbacbeta.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (c v1beta1ClusterRoleBinding) Name() string {
|
||||
return c.crb.Name
|
||||
}
|
||||
|
||||
func (c v1beta1ClusterRoleBinding) RoleRefName() string {
|
||||
return c.crb.RoleRef.Name
|
||||
}
|
||||
|
||||
func (c v1beta1ClusterRoleBinding) ServiceAccountSubjects(namespace string) []string {
|
||||
var saSubjects []string
|
||||
for _, s := range c.crb.Subjects {
|
||||
if s.Kind == rbac.ServiceAccountKind && s.Namespace == namespace {
|
||||
saSubjects = append(saSubjects, s.Name)
|
||||
}
|
||||
}
|
||||
return saSubjects
|
||||
}
|
||||
@@ -19,40 +19,24 @@ package actions
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
rbac "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/actionhelpers"
|
||||
)
|
||||
|
||||
// ServiceAccountAction implements ItemAction.
|
||||
type ServiceAccountAction struct {
|
||||
log logrus.FieldLogger
|
||||
clusterRoleBindings []ClusterRoleBinding
|
||||
clusterRoleBindings []actionhelpers.ClusterRoleBinding
|
||||
}
|
||||
|
||||
// NewServiceAccountAction creates a new ItemAction for service accounts.
|
||||
func NewServiceAccountAction(logger logrus.FieldLogger, clusterRoleBindingListers map[string]ClusterRoleBindingLister, discoveryHelper velerodiscovery.Helper) (*ServiceAccountAction, error) {
|
||||
// Look up the supported RBAC version
|
||||
var supportedAPI metav1.GroupVersionForDiscovery
|
||||
for _, ag := range discoveryHelper.APIGroups() {
|
||||
if ag.Name == rbac.GroupName {
|
||||
supportedAPI = ag.PreferredVersion
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
crbLister := clusterRoleBindingListers[supportedAPI.Version]
|
||||
|
||||
// This should be safe because the List call will return a 0-item slice
|
||||
// if there's no matching API version.
|
||||
crbs, err := crbLister.List()
|
||||
func NewServiceAccountAction(logger logrus.FieldLogger, clusterRoleBindingListers map[string]actionhelpers.ClusterRoleBindingLister, discoveryHelper velerodiscovery.Helper) (*ServiceAccountAction, error) {
|
||||
crbs, err := actionhelpers.ClusterRoleBindingsForAction(clusterRoleBindingListers, discoveryHelper)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -82,40 +66,5 @@ func (a *ServiceAccountAction) Execute(item runtime.Unstructured, backup *v1.Bac
|
||||
return nil, nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
var (
|
||||
namespace = objectMeta.GetNamespace()
|
||||
name = objectMeta.GetName()
|
||||
bindings = sets.NewString()
|
||||
roles = sets.NewString()
|
||||
)
|
||||
|
||||
for _, crb := range a.clusterRoleBindings {
|
||||
for _, s := range crb.ServiceAccountSubjects(namespace) {
|
||||
if s == name {
|
||||
a.log.Infof("Adding clusterrole %s and clusterrolebinding %s to additionalItems since serviceaccount %s/%s is a subject",
|
||||
crb.RoleRefName(), crb.Name(), namespace, name)
|
||||
|
||||
bindings.Insert(crb.Name())
|
||||
roles.Insert(crb.RoleRefName())
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var additionalItems []velero.ResourceIdentifier
|
||||
for binding := range bindings {
|
||||
additionalItems = append(additionalItems, velero.ResourceIdentifier{
|
||||
GroupResource: kuberesource.ClusterRoleBindings,
|
||||
Name: binding,
|
||||
})
|
||||
}
|
||||
|
||||
for role := range roles {
|
||||
additionalItems = append(additionalItems, velero.ResourceIdentifier{
|
||||
GroupResource: kuberesource.ClusterRoles,
|
||||
Name: role,
|
||||
})
|
||||
}
|
||||
|
||||
return item, additionalItems, nil
|
||||
return item, actionhelpers.RelatedItemsForServiceAccount(objectMeta, a.clusterRoleBindings, a.log), nil
|
||||
}
|
||||
|
||||
@@ -31,21 +31,22 @@ import (
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
velerotest "github.com/vmware-tanzu/velero/pkg/test"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/actionhelpers"
|
||||
)
|
||||
|
||||
func newV1ClusterRoleBindingList(rbacCRBList []rbac.ClusterRoleBinding) []ClusterRoleBinding {
|
||||
var crbs []ClusterRoleBinding
|
||||
func newV1ClusterRoleBindingList(rbacCRBList []rbac.ClusterRoleBinding) []actionhelpers.ClusterRoleBinding {
|
||||
var crbs []actionhelpers.ClusterRoleBinding
|
||||
for _, c := range rbacCRBList {
|
||||
crbs = append(crbs, v1ClusterRoleBinding{crb: c})
|
||||
crbs = append(crbs, actionhelpers.V1ClusterRoleBinding{Crb: c})
|
||||
}
|
||||
|
||||
return crbs
|
||||
}
|
||||
|
||||
func newV1beta1ClusterRoleBindingList(rbacCRBList []rbacbeta.ClusterRoleBinding) []ClusterRoleBinding {
|
||||
var crbs []ClusterRoleBinding
|
||||
func newV1beta1ClusterRoleBindingList(rbacCRBList []rbacbeta.ClusterRoleBinding) []actionhelpers.ClusterRoleBinding {
|
||||
var crbs []actionhelpers.ClusterRoleBinding
|
||||
for _, c := range rbacCRBList {
|
||||
crbs = append(crbs, v1beta1ClusterRoleBinding{crb: c})
|
||||
crbs = append(crbs, actionhelpers.V1beta1ClusterRoleBinding{Crb: c})
|
||||
}
|
||||
|
||||
return crbs
|
||||
@@ -55,10 +56,10 @@ type FakeV1ClusterRoleBindingLister struct {
|
||||
v1crbs []rbac.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (f FakeV1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) {
|
||||
var crbs []ClusterRoleBinding
|
||||
func (f FakeV1ClusterRoleBindingLister) List() ([]actionhelpers.ClusterRoleBinding, error) {
|
||||
var crbs []actionhelpers.ClusterRoleBinding
|
||||
for _, c := range f.v1crbs {
|
||||
crbs = append(crbs, v1ClusterRoleBinding{crb: c})
|
||||
crbs = append(crbs, actionhelpers.V1ClusterRoleBinding{Crb: c})
|
||||
}
|
||||
return crbs, nil
|
||||
}
|
||||
@@ -67,10 +68,10 @@ type FakeV1beta1ClusterRoleBindingLister struct {
|
||||
v1beta1crbs []rbacbeta.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (f FakeV1beta1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) {
|
||||
var crbs []ClusterRoleBinding
|
||||
func (f FakeV1beta1ClusterRoleBindingLister) List() ([]actionhelpers.ClusterRoleBinding, error) {
|
||||
var crbs []actionhelpers.ClusterRoleBinding
|
||||
for _, c := range f.v1beta1crbs {
|
||||
crbs = append(crbs, v1beta1ClusterRoleBinding{crb: c})
|
||||
crbs = append(crbs, actionhelpers.V1beta1ClusterRoleBinding{Crb: c})
|
||||
}
|
||||
return crbs, nil
|
||||
}
|
||||
@@ -93,21 +94,21 @@ func TestNewServiceAccountAction(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
version string
|
||||
expectedCRBs []ClusterRoleBinding
|
||||
expectedCRBs []actionhelpers.ClusterRoleBinding
|
||||
}{
|
||||
{
|
||||
name: "rbac v1 API instantiates an saAction",
|
||||
version: rbac.SchemeGroupVersion.Version,
|
||||
expectedCRBs: []ClusterRoleBinding{
|
||||
v1ClusterRoleBinding{
|
||||
crb: rbac.ClusterRoleBinding{
|
||||
expectedCRBs: []actionhelpers.ClusterRoleBinding{
|
||||
actionhelpers.V1ClusterRoleBinding{
|
||||
Crb: rbac.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "v1crb-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
v1ClusterRoleBinding{
|
||||
crb: rbac.ClusterRoleBinding{
|
||||
actionhelpers.V1ClusterRoleBinding{
|
||||
Crb: rbac.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "v1crb-2",
|
||||
},
|
||||
@@ -118,16 +119,16 @@ func TestNewServiceAccountAction(t *testing.T) {
|
||||
{
|
||||
name: "rbac v1beta1 API instantiates an saAction",
|
||||
version: rbacbeta.SchemeGroupVersion.Version,
|
||||
expectedCRBs: []ClusterRoleBinding{
|
||||
v1beta1ClusterRoleBinding{
|
||||
crb: rbacbeta.ClusterRoleBinding{
|
||||
expectedCRBs: []actionhelpers.ClusterRoleBinding{
|
||||
actionhelpers.V1beta1ClusterRoleBinding{
|
||||
Crb: rbacbeta.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "v1beta1crb-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
v1beta1ClusterRoleBinding{
|
||||
crb: rbacbeta.ClusterRoleBinding{
|
||||
actionhelpers.V1beta1ClusterRoleBinding{
|
||||
Crb: rbacbeta.ClusterRoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "v1beta1crb-2",
|
||||
},
|
||||
@@ -138,7 +139,7 @@ func TestNewServiceAccountAction(t *testing.T) {
|
||||
{
|
||||
name: "no RBAC API instantiates an saAction with empty slice",
|
||||
version: "",
|
||||
expectedCRBs: []ClusterRoleBinding{},
|
||||
expectedCRBs: []actionhelpers.ClusterRoleBinding{},
|
||||
},
|
||||
}
|
||||
// Set up all of our fakes outside the test loop
|
||||
@@ -171,10 +172,10 @@ func TestNewServiceAccountAction(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
clusterRoleBindingListers := map[string]ClusterRoleBindingLister{
|
||||
clusterRoleBindingListers := map[string]actionhelpers.ClusterRoleBindingLister{
|
||||
rbac.SchemeGroupVersion.Version: FakeV1ClusterRoleBindingLister{v1crbs: v1crbs},
|
||||
rbacbeta.SchemeGroupVersion.Version: FakeV1beta1ClusterRoleBindingLister{v1beta1crbs: v1beta1crbs},
|
||||
"": noopClusterRoleBindingLister{},
|
||||
"": actionhelpers.NoopClusterRoleBindingLister{},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
Reference in New Issue
Block a user