move groupresource resolution into discovery helper

Signed-off-by: Steve Kriss <steve@heptio.com>
This commit is contained in:
Steve Kriss
2017-09-01 14:39:04 -07:00
parent f438c226e3
commit 907ae6c5b0
5 changed files with 118 additions and 79 deletions

View File

@@ -76,7 +76,7 @@ func NewKubernetesBackupper(
dynamicFactory client.DynamicFactory,
actions map[string]Action,
) (Backupper, error) {
resolvedActions, err := resolveActions(discoveryHelper.Mapper(), actions)
resolvedActions, err := resolveActions(discoveryHelper, actions)
if err != nil {
return nil, err
}
@@ -91,11 +91,11 @@ func NewKubernetesBackupper(
// resolveActions resolves the string-based map of group-resources to actions and returns a map of
// schema.GroupResources to actions.
func resolveActions(mapper meta.RESTMapper, actions map[string]Action) (map[schema.GroupResource]Action, error) {
func resolveActions(helper discovery.Helper, actions map[string]Action) (map[schema.GroupResource]Action, error) {
ret := make(map[schema.GroupResource]Action)
for resource, action := range actions {
gr, err := resolveGroupResource(mapper, resource)
gr, err := helper.ResolveGroupResource(resource)
if err != nil {
return nil, err
}
@@ -105,46 +105,22 @@ func resolveActions(mapper meta.RESTMapper, actions map[string]Action) (map[sche
return ret, nil
}
// resolveResources uses the RESTMapper to resolve resources to their fully-qualified group-resource
// names. fn is invoked for each resolved resource. resolveResources returns a list of any resources that failed to resolve.
func (ctx *backupContext) resolveResources(mapper meta.RESTMapper, resources []string, allowAll bool, fn func(string)) {
for _, resource := range resources {
if allowAll && resource == "*" {
fn("*")
return
}
gr, err := resolveGroupResource(mapper, resource)
if err != nil {
ctx.log("Unable to resolve resource %q: %v", resource, err)
continue
}
fn(gr.String())
}
}
// getResourceIncludesExcludes takes the lists of resources to include and exclude from the
// backup, uses the discovery helper to resolve them to fully-qualified group-resource names, and returns
// an IncludesExcludes list.
func (ctx *backupContext) getResourceIncludesExcludes(helper discovery.Helper, includes, excludes []string) *collections.IncludesExcludes {
return collections.GenerateIncludesExcludes(
includes,
excludes,
func(item string) (string, error) {
gr, err := helper.ResolveGroupResource(item)
if err != nil {
return "", err
}
// getResourceIncludesExcludes takes the lists of resources to include and exclude, uses the
// RESTMapper to resolve them to fully-qualified group-resource names, and returns an
// IncludesExcludes list.
func (ctx *backupContext) getResourceIncludesExcludes(mapper meta.RESTMapper, includes, excludes []string) *collections.IncludesExcludes {
resources := collections.NewIncludesExcludes()
ctx.resolveResources(mapper, includes, true, func(s string) { resources.Includes(s) })
ctx.resolveResources(mapper, excludes, false, func(s string) { resources.Excludes(s) })
ctx.log("Including resources: %v", strings.Join(resources.GetIncludes(), ", "))
ctx.log("Excluding resources: %v", strings.Join(resources.GetExcludes(), ", "))
return resources
}
// resolveGroupResource uses the RESTMapper to resolve resource to a fully-qualified
// schema.GroupResource. If the RESTMapper is unable to do so, an error is returned instead.
func resolveGroupResource(mapper meta.RESTMapper, resource string) (schema.GroupResource, error) {
gvr, err := mapper.ResourceFor(schema.ParseGroupResource(resource).WithVersion(""))
if err != nil {
return schema.GroupResource{}, err
}
return gvr.GroupResource(), nil
return gr.String(), nil
},
)
}
// getNamespaceIncludesExcludes returns an IncludesExcludes list containing which namespaces to
@@ -206,7 +182,7 @@ func (kb *kubernetesBackupper) Backup(backup *api.Backup, backupFile, logFile io
ctx.log("Starting backup")
ctx.resourceIncludesExcludes = ctx.getResourceIncludesExcludes(kb.discoveryHelper.Mapper(), backup.Spec.IncludedResources, backup.Spec.ExcludedResources)
ctx.resourceIncludesExcludes = ctx.getResourceIncludesExcludes(kb.discoveryHelper, backup.Spec.IncludedResources, backup.Spec.ExcludedResources)
for _, group := range kb.discoveryHelper.Resources() {
ctx.log("Processing group %s", group.GroupVersion)

View File

@@ -104,16 +104,18 @@ func TestResolveActions(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mapper := &FakeMapper{
Resources: map[schema.GroupVersionResource]schema.GroupVersionResource{
schema.GroupVersionResource{Resource: "foo"}: schema.GroupVersionResource{Group: "somegroup", Resource: "foodies"},
schema.GroupVersionResource{Resource: "fie"}: schema.GroupVersionResource{Group: "somegroup", Resource: "fields"},
schema.GroupVersionResource{Resource: "bar"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "barnacles"},
schema.GroupVersionResource{Resource: "baz"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "bazaars"},
dh := &FakeDiscoveryHelper{
RESTMapper: &FakeMapper{
Resources: map[schema.GroupVersionResource]schema.GroupVersionResource{
schema.GroupVersionResource{Resource: "foo"}: schema.GroupVersionResource{Group: "somegroup", Resource: "foodies"},
schema.GroupVersionResource{Resource: "fie"}: schema.GroupVersionResource{Group: "somegroup", Resource: "fields"},
schema.GroupVersionResource{Resource: "bar"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "barnacles"},
schema.GroupVersionResource{Resource: "baz"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "bazaars"},
},
},
}
actual, err := resolveActions(mapper, test.input)
actual, err := resolveActions(dh, test.input)
gotError := err != nil
if e, a := test.expectError, gotError; e != a {
@@ -176,12 +178,14 @@ func TestGetResourceIncludesExcludes(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
mapper := &FakeMapper{
Resources: map[schema.GroupVersionResource]schema.GroupVersionResource{
schema.GroupVersionResource{Resource: "foo"}: schema.GroupVersionResource{Group: "somegroup", Resource: "foodies"},
schema.GroupVersionResource{Resource: "fie"}: schema.GroupVersionResource{Group: "somegroup", Resource: "fields"},
schema.GroupVersionResource{Resource: "bar"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "barnacles"},
schema.GroupVersionResource{Resource: "baz"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "bazaars"},
dh := &FakeDiscoveryHelper{
RESTMapper: &FakeMapper{
Resources: map[schema.GroupVersionResource]schema.GroupVersionResource{
schema.GroupVersionResource{Resource: "foo"}: schema.GroupVersionResource{Group: "somegroup", Resource: "foodies"},
schema.GroupVersionResource{Resource: "fie"}: schema.GroupVersionResource{Group: "somegroup", Resource: "fields"},
schema.GroupVersionResource{Resource: "bar"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "barnacles"},
schema.GroupVersionResource{Resource: "baz"}: schema.GroupVersionResource{Group: "anothergroup", Resource: "bazaars"},
},
},
}
@@ -190,7 +194,7 @@ func TestGetResourceIncludesExcludes(t *testing.T) {
logger: &logger{w: b},
}
actual := ctx.getResourceIncludesExcludes(mapper, test.includes, test.excludes)
actual := ctx.getResourceIncludesExcludes(dh, test.includes, test.excludes)
sort.Strings(test.expectedIncludes)
actualIncludes := actual.GetIncludes()
@@ -236,23 +240,6 @@ func TestGetNamespaceIncludesExcludes(t *testing.T) {
}
}
type fakeDiscoveryHelper struct {
resources []*metav1.APIResourceList
mapper meta.RESTMapper
}
func (dh *fakeDiscoveryHelper) Resources() []*metav1.APIResourceList {
return dh.resources
}
func (dh *fakeDiscoveryHelper) Mapper() meta.RESTMapper {
return dh.mapper
}
func (dh *fakeDiscoveryHelper) Refresh() error {
return nil
}
func TestBackupMethod(t *testing.T) {
// TODO ensure LabelSelector is passed through to the List() calls
backup := &v1.Backup{
@@ -303,15 +290,15 @@ func TestBackupMethod(t *testing.T) {
ShortNames: []string{"csr"},
}
discoveryHelper := &fakeDiscoveryHelper{
mapper: &FakeMapper{
discoveryHelper := &FakeDiscoveryHelper{
RESTMapper: &FakeMapper{
Resources: map[schema.GroupVersionResource]schema.GroupVersionResource{
schema.GroupVersionResource{Resource: "cm"}: schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configmaps"},
schema.GroupVersionResource{Resource: "csr"}: schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"},
schema.GroupVersionResource{Resource: "roles"}: schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1beta1", Resource: "roles"},
},
},
resources: []*metav1.APIResourceList{
ResourceList: []*metav1.APIResourceList{
{
GroupVersion: "v1",
APIResources: []metav1.APIResource{configMapsResource, podsResource},
@@ -829,8 +816,8 @@ func TestBackupResource(t *testing.T) {
}
}
discoveryHelper := &fakeDiscoveryHelper{
mapper: &FakeMapper{
discoveryHelper := &FakeDiscoveryHelper{
RESTMapper: &FakeMapper{
Resources: map[schema.GroupVersionResource]schema.GroupVersionResource{
schema.GroupVersionResource{Resource: "certificatesigningrequests"}: schema.GroupVersionResource{Group: "certificates.k8s.io", Version: "v1beta1", Resource: "certificatesigningrequests"},
schema.GroupVersionResource{Resource: "other"}: schema.GroupVersionResource{Group: "somegroup", Version: "someversion", Resource: "otherthings"},

View File

@@ -44,6 +44,10 @@ type Helper interface {
// Refresh pulls an updated set of Ark-backuppable resources from the
// discovery API.
Refresh() error
// ResolveGroupResource uses the RESTMapper to resolve resource to a fully-qualified
// schema.GroupResource. If the RESTMapper is unable to do so, an error is returned instead.
ResolveGroupResource(resource string) (schema.GroupResource, error)
}
type helper struct {
@@ -139,3 +143,11 @@ func (h *helper) Resources() []*metav1.APIResourceList {
defer h.lock.RUnlock()
return h.resources
}
func (h *helper) ResolveGroupResource(resource string) (schema.GroupResource, error) {
gvr, err := h.mapper.ResourceFor(schema.ParseGroupResource(resource).WithVersion(""))
if err != nil {
return schema.GroupResource{}, err
}
return gvr.GroupResource(), nil
}

View File

@@ -20,6 +20,8 @@ import (
"errors"
"fmt"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/sets"
)
@@ -104,3 +106,34 @@ func ValidateIncludesExcludes(includesList, excludesList []string) []error {
return errs
}
func GenerateIncludesExcludes(includes []string, excludes []string, mapFunc func(string) (string, error)) *IncludesExcludes {
res := NewIncludesExcludes()
for _, item := range includes {
if item == "*" {
res.Includes(item)
return res
}
key, err := mapFunc(item)
if err != nil {
glog.Errorf("unable to include item %q: %v", item, err)
continue
}
res.Includes(key)
}
for _, item := range excludes {
key, err := mapFunc(item)
if err != nil {
glog.Errorf("unable to exclude item %q: %v", item, err)
continue
}
res.Excludes(key)
}
return res
}

View File

@@ -0,0 +1,31 @@
package test
import (
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type FakeDiscoveryHelper struct {
ResourceList []*metav1.APIResourceList
RESTMapper meta.RESTMapper
}
func (dh *FakeDiscoveryHelper) Mapper() meta.RESTMapper {
return dh.RESTMapper
}
func (dh *FakeDiscoveryHelper) Resources() []*metav1.APIResourceList {
return dh.ResourceList
}
func (dh *FakeDiscoveryHelper) Refresh() error {
return nil
}
func (dh *FakeDiscoveryHelper) ResolveGroupResource(resource string) (schema.GroupResource, error) {
gvr, err := dh.RESTMapper.ResourceFor(schema.ParseGroupResource(resource).WithVersion(""))
if err != nil {
return schema.GroupResource{}, err
}
return gvr.GroupResource(), nil
}