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

Validate the label/annotation in validate method of Kanister function #3032

Merged
merged 11 commits into from
Aug 20, 2024
Merged
105 changes: 105 additions & 0 deletions pkg/blueprint/validate/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

kanister "github.com/kanisterio/kanister/pkg"
crv1alpha1 "github.com/kanisterio/kanister/pkg/apis/cr/v1alpha1"
"github.com/kanisterio/kanister/pkg/function"
"github.com/kanisterio/kanister/pkg/param"
"github.com/kanisterio/kanister/pkg/utils"
)
Expand Down Expand Up @@ -343,6 +344,110 @@ func (v *ValidateBlueprint) TestValidateNonDefaultVersion(c *C) {
}
}

func (v *ValidateBlueprint) TestValidateAnnLabelArgs(c *C) {
for _, tc := range []struct {
labels interface{}
annotations interface{}
error string
}{
{
labels: map[string]interface{}{
"key": "value",
},
error: "",
},
{
annotations: map[string]interface{}{
"key": "value",
},
error: "",
},
{
labels: map[string]interface{}{
"key": "value",
},
annotations: map[string]interface{}{
"key": "value",
},
error: "",
},
{
labels: map[string]interface{}{
"key$": "value",
},
annotations: map[string]interface{}{
"key": "value",
},
error: "label key 'key$' failed validation",
},
{
labels: map[string]interface{}{
"key*": "value",
},
annotations: map[string]interface{}{
"key": "value",
},
error: "label key 'key*' failed validation",
},
{
labels: map[string]interface{}{
"key": "value$",
},
annotations: map[string]interface{}{
"key": "value",
},
error: "label value 'value$' failed validation",
},
{
labels: map[string]interface{}{
"key": "value",
},
annotations: map[string]interface{}{
"key$": "value",
},
error: "annotation key 'key$' failed validation",
},
{
labels: map[string]interface{}{
"key": "value",
},
annotations: map[string]interface{}{
"key": "value$",
},
viveksinghggits marked this conversation as resolved.
Show resolved Hide resolved
error: "",
},
{
labels: map[string]interface{}{
"key": "",
},
annotations: map[string]interface{}{
"key": "",
},
error: "",
},
} {
bp := blueprint()
bp.Actions["backup"].Phases = []crv1alpha1.BlueprintPhase{
{
Func: "KubeTask",
Name: "backup",
Args: map[string]interface{}{
function.PodLabelsArg: tc.labels,
function.PodAnnotationsArg: tc.annotations,
"image": "",
"command": "",
},
},
}
err := Do(bp, kanister.DefaultVersion)
if tc.error != "" {
c.Assert(strings.Contains(err.Error(), tc.error), Equals, true)
} else {
c.Assert(err, Equals, nil)
}
}
}

func (v *ValidateBlueprint) TestValidatePhaseNames(c *C) {
for _, tc := range []BlueprintTest{
{
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/backup_data_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,10 @@ func (*BackupDataStatsFunc) Arguments() []string {
}

func (b *BackupDataStatsFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(b.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(b.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/checkRepository.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ func (*CheckRepositoryFunc) Arguments() []string {
}

func (c *CheckRepositoryFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(c.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(c.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/copy_volume_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ func (*copyVolumeDataFunc) Arguments() []string {
}

func (c *copyVolumeDataFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(c.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(c.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/delete_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ func (*deleteDataFunc) Arguments() []string {
}

func (d *deleteDataFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(d.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(d.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/delete_data_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,10 @@ func (*deleteDataAllFunc) Arguments() []string {
}

func (d *deleteDataAllFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(d.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(d.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/delete_data_using_kopia_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ func (*deleteDataUsingKopiaServerFunc) Arguments() []string {
}

func (d *deleteDataUsingKopiaServerFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(d.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(d.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/export_rds_snapshot_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,10 @@ func (*exportRDSSnapshotToLocationFunc) Arguments() []string {
}

func (e *exportRDSSnapshotToLocationFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(e.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(e.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/kube_task.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ func (*kubeTaskFunc) Arguments() []string {
}

func (ktf *kubeTaskFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(ktf.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(ktf.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/prepare_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ func (*prepareDataFunc) Arguments() []string {
}

func (p *prepareDataFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(p.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(p.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/restore_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,10 @@ func (*restoreDataFunc) Arguments() []string {
}

func (r *restoreDataFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(r.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(r.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/restore_data_all.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ func (*restoreDataAllFunc) Arguments() []string {
}

func (r *restoreDataAllFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(r.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(r.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/restore_data_using_kopia_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ func (*restoreDataUsingKopiaServerFunc) Arguments() []string {
}

func (r *restoreDataUsingKopiaServerFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(r.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(r.Arguments(), args); err != nil {
return err
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/function/restore_rds_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ func (*restoreRDSSnapshotFunc) Arguments() []string {
}

func (r *restoreRDSSnapshotFunc) Validate(args map[string]any) error {
if err := ValidatePodLabelsAndAnnotations(r.Name(), args); err != nil {
return err
}

if err := utils.CheckSupportedArgs(r.Arguments(), args); err != nil {
return err
}
Expand Down
90 changes: 90 additions & 0 deletions pkg/function/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,18 @@ package function
import (
"bytes"
"context"
"fmt"
"path"
"strings"

awssdk "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
rdserr "github.com/aws/aws-sdk-go/service/rds"
"github.com/kanisterio/errkit"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/client-go/kubernetes"

crv1alpha1 "github.com/kanisterio/kanister/pkg/apis/cr/v1alpha1"
Expand Down Expand Up @@ -303,3 +306,90 @@ func GetRDSAuroraDBSubnetGroup(ctx context.Context, rdsCli *rds.RDS, instanceID
}
return desc.DBClusters[0].DBSubnetGroup, nil
}

// ValidatePodLabelsAndAnnotations validates the labels and annotations that are
// passed to a Kanister function (`funcName`) using `podLabels` and `podAnnotations` args.
func ValidatePodLabelsAndAnnotations(funcName string, args map[string]any) error {
labels, err := PodLabelsFromFunctionArgs(args)
if err != nil {
return errkit.Wrap(err, "Kanister function validation failed, while getting pod labels from function args", "funcName", funcName)
}

if err = ValidateLabels(labels); err != nil {
return errkit.Wrap(err, "Kanister function validation failed, while validating labels", "funcName", funcName)
}

annotations, err := PodAnnotationsFromFunctionArgs(args)
if err != nil {
return errkit.Wrap(err, "Kanister function validation failed, while getting pod annotations from function args", "funcName", funcName)
}
if err = ValidateAnnotations(annotations); err != nil {
return errkit.Wrap(err, "Kanister function validation failed, while validating annotations", "funcName", funcName)
}
return nil
}

func PodLabelsFromFunctionArgs(args map[string]any) (map[string]string, error) {
for k, v := range args {
if k == PodLabelsArg && v != nil {
labels, ok := v.(map[string]interface{})
if !ok {
return nil, errkit.New("podLabels are not in correct format. Expected format is map[string]interface{}.")
}
return mapStringInterfaceToString(labels), nil
}
}
return nil, nil
}

// mapStringInterfaceToString accepts a map of `string` and `interface{}` and creates
// a map of `string` and `string` from passed map and returns that.
// If a value in the passed map is not of type `string`, it will be skipped.
func mapStringInterfaceToString(m map[string]interface{}) map[string]string {
res := map[string]string{}
for k, v := range m {
switch v := v.(type) {
case string:
res[k] = v
default:
log.Info().Print("Map value is not of type string, while converting map[string]interface{} to map[string]string. Skipping.", map[string]interface{}{"value": v})
}
}
return res
}

func PodAnnotationsFromFunctionArgs(args map[string]any) (map[string]string, error) {
for k, v := range args {
if k == PodAnnotationsArg && v != nil {
annotations, ok := v.(map[string]interface{})
if !ok {
return nil, errkit.New("podAnnotations are not in correct format. expected format is map[string]string.")
}
return mapStringInterfaceToString(annotations), nil
}
}
return nil, nil
}

func ValidateLabels(labels map[string]string) error {
for k, v := range labels {
if errs := validation.IsQualifiedName(k); len(errs) > 0 {
return errors.New(fmt.Sprintf("label key '%s' failed validation. %s", k, errs))
}

if errs := validation.IsValidLabelValue(v); len(errs) > 0 {
return errors.New(fmt.Sprintf("label value '%s' failed validation. %s", v, errs))
}
}
return nil
}

func ValidateAnnotations(annotations map[string]string) error {
for k := range annotations {
if errs := validation.IsQualifiedName(k); len(errs) > 0 {
return errors.New(fmt.Sprintf("annotation key '%s' failed validation. %s", k, errs))
}
}
// annotation values don't actually have a strict format
viveksinghggits marked this conversation as resolved.
Show resolved Hide resolved
return nil
}