diff --git a/changelogs/unreleased/6151-tkaovila b/changelogs/unreleased/6151-tkaovila new file mode 100644 index 000000000..08050fd5e --- /dev/null +++ b/changelogs/unreleased/6151-tkaovila @@ -0,0 +1 @@ +Make GetPluginConfig accessible from other packages. \ No newline at end of file diff --git a/pkg/plugin/framework/common/plugin_config.go b/pkg/plugin/framework/common/plugin_config.go new file mode 100644 index 000000000..f2cb133ec --- /dev/null +++ b/pkg/plugin/framework/common/plugin_config.go @@ -0,0 +1,58 @@ +/* +Copyright 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 common + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" +) + +func PluginConfigLabelSelector(kind PluginKind, name string) string { + return fmt.Sprintf("velero.io/plugin-config=true,%s=%s", name, kind) +} + +func GetPluginConfig(kind PluginKind, name string, client corev1client.ConfigMapInterface) (*corev1.ConfigMap, error) { + opts := metav1.ListOptions{ + // velero.io/plugin-config: true + // velero.io/pod-volume-restore: RestoreItemAction + LabelSelector: PluginConfigLabelSelector(kind, name), + } + + list, err := client.List(context.Background(), opts) + if err != nil { + return nil, errors.WithStack(err) + } + + if len(list.Items) == 0 { + return nil, nil + } + + if len(list.Items) > 1 { + var items []string + for _, item := range list.Items { + items = append(items, item.Name) + } + return nil, errors.Errorf("found more than one ConfigMap matching label selector %q: %v", opts.LabelSelector, items) + } + + return &list.Items[0], nil +} diff --git a/pkg/plugin/framework/common/plugin_config_test.go b/pkg/plugin/framework/common/plugin_config_test.go new file mode 100644 index 000000000..107ff9591 --- /dev/null +++ b/pkg/plugin/framework/common/plugin_config_test.go @@ -0,0 +1,122 @@ +/* +Copyright 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 common + +import ( + "reflect" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/fake" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func TestGetPluginConfig(t *testing.T) { + type args struct { + kind PluginKind + name string + objects []runtime.Object + } + pluginLabelsSet, _ := labels.ConvertSelectorToLabelsMap(PluginConfigLabelSelector(PluginKindRestoreItemAction, "foo")) + testConfigMap := &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-config", + Namespace: velerov1.DefaultNamespace, + Labels: pluginLabelsSet, + }, + } + tests := []struct { + name string + args args + want *corev1.ConfigMap + wantErr bool + }{ + { + name: "should return nil if no config map found", + args: args{ + kind: PluginKindRestoreItemAction, + name: "foo", + objects: []runtime.Object{}, + }, + want: nil, + wantErr: false, + }, + { + name: "should return error if more than one config map found", + args: args{ + kind: PluginKindRestoreItemAction, + name: "foo", + objects: []runtime.Object{ + &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-config", + Namespace: velerov1.DefaultNamespace, + Labels: pluginLabelsSet, + }, + }, + &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "foo-config-duplicate", + Namespace: velerov1.DefaultNamespace, + Labels: pluginLabelsSet, + }, + }, + }, + }, + want: nil, + wantErr: true, + }, + { + name: "should return pointer to configmap if only one config map with label found", + args: args{ + kind: PluginKindRestoreItemAction, + name: "foo", + objects: []runtime.Object{ + testConfigMap, + }, + }, + want: testConfigMap, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeClient := fake.NewSimpleClientset(tt.args.objects...) + got, err := GetPluginConfig(tt.args.kind, tt.args.name, fakeClient.CoreV1().ConfigMaps(velerov1.DefaultNamespace)) + if (err != nil) != tt.wantErr { + t.Errorf("GetPluginConfig() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetPluginConfig() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/pkg/restore/change_pvc_node_selector.go b/pkg/restore/change_pvc_node_selector.go index c051b918b..5934cd266 100644 --- a/pkg/restore/change_pvc_node_selector.go +++ b/pkg/restore/change_pvc_node_selector.go @@ -140,7 +140,7 @@ func (p *ChangePVCNodeSelectorAction) Execute(input *velero.RestoreItemActionExe func getNewNodeFromConfigMap(client corev1client.ConfigMapInterface, node string) (string, error) { // fetch node mapping from configMap - config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-pvc-node-selector", client) + config, err := common.GetPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-pvc-node-selector", client) if err != nil { return "", err } diff --git a/pkg/restore/change_storageclass_action.go b/pkg/restore/change_storageclass_action.go index a02b7c484..91384c64e 100644 --- a/pkg/restore/change_storageclass_action.go +++ b/pkg/restore/change_storageclass_action.go @@ -69,7 +69,7 @@ func (a *ChangeStorageClassAction) Execute(input *velero.RestoreItemActionExecut defer a.logger.Info("Done executing ChangeStorageClassAction") a.logger.Debug("Getting plugin config") - config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-storage-class", a.configMapClient) + config, err := common.GetPluginConfig(common.PluginKindRestoreItemAction, "velero.io/change-storage-class", a.configMapClient) if err != nil { return nil, err } diff --git a/pkg/restore/pod_volume_restore_action.go b/pkg/restore/pod_volume_restore_action.go index 3f419c0ac..f7fb43084 100644 --- a/pkg/restore/pod_volume_restore_action.go +++ b/pkg/restore/pod_volume_restore_action.go @@ -24,7 +24,6 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" @@ -108,7 +107,7 @@ func (a *PodVolumeRestoreAction) Execute(input *velero.RestoreItemActionExecuteI // TODO we might want/need to get plugin config at the top of this method at some point; for now, wait // until we know we're doing a restore before getting config. log.Debugf("Getting plugin config") - config, err := getPluginConfig(common.PluginKindRestoreItemAction, "velero.io/pod-volume-restore", a.client) + config, err := common.GetPluginConfig(common.PluginKindRestoreItemAction, "velero.io/pod-volume-restore", a.client) if err != nil { return nil, err } @@ -260,35 +259,6 @@ func getSecurityContext(log logrus.FieldLogger, config *corev1.ConfigMap) (strin config.Data["secCtx"] } -// TODO eventually this can move to pkg/plugin/framework since it'll be used across multiple -// plugins. -func getPluginConfig(kind common.PluginKind, name string, client corev1client.ConfigMapInterface) (*corev1.ConfigMap, error) { - opts := metav1.ListOptions{ - // velero.io/plugin-config: true - // velero.io/pod-volume-restore: RestoreItemAction - LabelSelector: fmt.Sprintf("velero.io/plugin-config,%s=%s", name, kind), - } - - list, err := client.List(context.TODO(), opts) - if err != nil { - return nil, errors.WithStack(err) - } - - if len(list.Items) == 0 { - return nil, nil - } - - if len(list.Items) > 1 { - var items []string - for _, item := range list.Items { - items = append(items, item.Name) - } - return nil, errors.Errorf("found more than one ConfigMap matching label selector %q: %v", opts.LabelSelector, items) - } - - return &list.Items[0], nil -} - func newRestoreInitContainerBuilder(image, restoreUID string) *builder.ContainerBuilder { return builder.ForContainer(restorehelper.WaitInitContainer, image). Args(restoreUID).