Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add label selector in Resource Modifiers #6704

Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/unreleased/6704-27149chen
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This pr made some improvements in Resource Modifiers:
1. add label selector
2. change the field name from groupKind to groupResource
anshulahuja98 marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 4 additions & 4 deletions design/Implemented/json-substitution-action-design.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Currently velero supports substituting certain values in the K8s resources durin
<!-- ## Background -->

## Goals
- Allow the user to specify a GroupKind, Name(optional), JSON patch for modification.
- Allow the user to specify a GroupResource, Name(optional), JSON patch for modification.
- Allow the user to specify multiple JSON patch.

## Non Goals
Expand Down Expand Up @@ -74,7 +74,7 @@ velero restore create --from-backup backup-1 --resource-modifier-configmap resou

### Resource Modifier ConfigMap Structure
- User first needs to provide details on which resources the JSON Substitutions need to be applied.
- For this the user will provide 4 inputs - Namespaces(for NS Scoped resources), GroupKind (kind.group format similar to includeResources field in velero) and Name Regex(optional).
- For this the user will provide 4 inputs - Namespaces(for NS Scoped resources), GroupResource (resource.group format similar to includeResources field in velero) and Name Regex(optional).
- If the user does not provide the Name, the JSON Substitutions will be applied to all the resources of the given Group and Kind under the given namespaces.

- Further the use will specify the JSON Patch using the structure of kubectl's "JSON Patch" based inputs.
Expand All @@ -83,7 +83,7 @@ velero restore create --from-backup backup-1 --resource-modifier-configmap resou
version: v1
resourceModifierRules:
- conditions:
groupKind: persistentvolumeclaims
groupResource: persistentvolumeclaims
resourceNameRegex: "mysql.*"
namespaces:
- bar
Expand Down Expand Up @@ -119,7 +119,7 @@ kubectl create cm <configmap-name> --from-file <yaml-file> -n velero
version: v1
resourceModifierRules:
- conditions:
groupKind: persistentvolumeclaims.storage.k8s.io
groupResource: persistentvolumeclaims.storage.k8s.io
resourceNameRegex: ".*"
namespaces:
- bar
Expand Down
58 changes: 33 additions & 25 deletions internal/resourcemodifiers/resource_modifiers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

import (
"fmt"
"io"
"regexp"
"strconv"
"strings"

jsonpatch "github.com/evanphx/json-patch"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
v1 "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/labels"
"sigs.k8s.io/yaml"

"github.com/vmware-tanzu/velero/pkg/util/collections"
)
Expand All @@ -23,26 +24,27 @@
)

type JSONPatch struct {
Operation string `yaml:"operation"`
From string `yaml:"from,omitempty"`
Path string `yaml:"path"`
Value string `yaml:"value,omitempty"`
Operation string `json:"operation"`
From string `json:"from,omitempty"`
Path string `json:"path"`
Value string `json:"value,omitempty"`
}

type Conditions struct {
Namespaces []string `yaml:"namespaces,omitempty"`
GroupKind string `yaml:"groupKind"`
ResourceNameRegex string `yaml:"resourceNameRegex"`
Namespaces []string `json:"namespaces,omitempty"`
GroupResource string `json:"groupResource"`
ResourceNameRegex string `json:"resourceNameRegex,omitempty"`
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
}

type ResourceModifierRule struct {
Conditions Conditions `yaml:"conditions"`
Patches []JSONPatch `yaml:"patches"`
Conditions Conditions `json:"conditions"`
Patches []JSONPatch `json:"patches"`
}

type ResourceModifiers struct {
Version string `yaml:"version"`
ResourceModifierRules []ResourceModifierRule `yaml:"resourceModifierRules"`
Version string `json:"version"`
ResourceModifierRules []ResourceModifierRule `json:"resourceModifierRules"`
}

func GetResourceModifiersFromConfig(cm *v1.ConfigMap) (*ResourceModifiers, error) {
Expand All @@ -58,7 +60,7 @@
yamlData = v
}

resModifiers, err := unmarshalResourceModifiers(&yamlData)
resModifiers, err := unmarshalResourceModifiers([]byte(yamlData))
if err != nil {
return nil, errors.WithStack(err)
}
Expand All @@ -83,9 +85,11 @@
if !namespaceInclusion.ShouldInclude(obj.GetNamespace()) {
return nil
}
if !strings.EqualFold(groupResource, r.Conditions.GroupKind) {

if r.Conditions.GroupResource != groupResource {
return nil
}

if r.Conditions.ResourceNameRegex != "" {
match, err := regexp.MatchString(r.Conditions.ResourceNameRegex, obj.GetName())
if err != nil {
Expand All @@ -95,6 +99,17 @@
return nil
}
}

if r.Conditions.LabelSelector != nil {
selector, err := metav1.LabelSelectorAsSelector(r.Conditions.LabelSelector)
if err != nil {
return errors.Errorf("error in creating label selector %s", err.Error())
}

Check warning on line 107 in internal/resourcemodifiers/resource_modifiers.go

View check run for this annotation

Codecov / codecov/patch

internal/resourcemodifiers/resource_modifiers.go#L106-L107

Added lines #L106 - L107 were not covered by tests
if !selector.Matches(labels.Set(obj.GetLabels())) {
return nil
}
}

patches, err := r.PatchArrayToByteArray()
if err != nil {
return err
Expand All @@ -107,7 +122,7 @@
return nil
}

// convert all JsonPatch to string array with the format of jsonpatch.Patch and then convert it to byte array
// PatchArrayToByteArray converts all JsonPatch to string array with the format of jsonpatch.Patch and then convert it to byte array
func (r *ResourceModifierRule) PatchArrayToByteArray() ([]byte, error) {
var patches []string
for _, patch := range r.Patches {
Expand Down Expand Up @@ -148,22 +163,15 @@
return nil
}

func unmarshalResourceModifiers(yamlData *string) (*ResourceModifiers, error) {
func unmarshalResourceModifiers(yamlData []byte) (*ResourceModifiers, error) {
resModifiers := &ResourceModifiers{}
err := decodeStruct(strings.NewReader(*yamlData), resModifiers)
err := yaml.UnmarshalStrict(yamlData, resModifiers)
if err != nil {
return nil, fmt.Errorf("failed to decode yaml data into resource modifiers %v", err)
}
return resModifiers, nil
}

// decodeStruct restrict validate the keys in decoded mappings to exist as fields in the struct being decoded into
func decodeStruct(r io.Reader, s interface{}) error {
dec := yaml.NewDecoder(r)
dec.KnownFields(true)
return dec.Decode(s)
}

func addQuotes(value string) bool {
if value == "" {
return true
Expand Down
Loading
Loading