This is an automated email from the ASF dual-hosted git repository.

zhongxjian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-kubernetes.git


The following commit(s) were added to refs/heads/master by this push:
     new b4a0618d [operator] Add validate command logic v3
b4a0618d is described below

commit b4a0618d0d7007b85760af5089dd16b84a7dca8f
Author: mfordjody <[email protected]>
AuthorDate: Thu Dec 26 11:11:22 2024 +0800

    [operator] Add validate command logic v3
---
 dubboctl/pkg/validate/validate.go          | 143 ++++++++++++++++++++++++-----
 operator/pkg/apis/validation/validation.go |  62 +++++++++++++
 2 files changed, 182 insertions(+), 23 deletions(-)

diff --git a/dubboctl/pkg/validate/validate.go 
b/dubboctl/pkg/validate/validate.go
index 48b6536d..bc7c6750 100644
--- a/dubboctl/pkg/validate/validate.go
+++ b/dubboctl/pkg/validate/validate.go
@@ -5,22 +5,23 @@ import (
        "errors"
        "fmt"
        "github.com/apache/dubbo-kubernetes/dubboctl/pkg/cli"
-       "github.com/apache/dubbo-kubernetes/operator/cmd/validation"
        operator "github.com/apache/dubbo-kubernetes/operator/pkg/apis"
-       "github.com/apache/dubbo-kubernetes/operator/pkg/config"
+       operatorvalidate 
"github.com/apache/dubbo-kubernetes/operator/pkg/apis/validation"
+       "github.com/apache/dubbo-kubernetes/pkg/config/validation"
+       "github.com/apache/dubbo-kubernetes/pkg/util/slices"
        "github.com/hashicorp/go-multierror"
        "github.com/spf13/cobra"
        "io"
        "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
        "k8s.io/apimachinery/pkg/util/yaml"
+       "os"
+       "path/filepath"
+       "strings"
 )
 
 var (
        errFiles = errors.New(`error: you must specify resources by --filename.
-Example resource specifications include:
-   '-f default.yaml'
-   '--filename=default.json'`)
-
+Example resource specifications include: '-f default.yaml'`)
        validFields = map[string]struct{}{
                "apiVersion": {},
                "kind":       {},
@@ -28,14 +29,15 @@ Example resource specifications include:
                "spec":       {},
                "status":     {},
        }
+       fileExtensions = []string{".json", ".yaml", ".yml"}
 )
 
 type validator struct{}
 
-func (v *validator) validateFile(path string, dubboNamespace *string, 
defaultNamespace string, reader io.Reader, writer io.Writer) 
(validation.Warning, error) {
+func (v *validator) validateFile(path string, dubboNamespace *string, reader 
io.Reader, writer io.Writer) (validation.Warning, error) {
        yamlReader := yaml.NewYAMLReader(bufio.NewReader(reader))
        var errs error
-       var warnings validation.Warnings
+       var warnings validation.Warning
        for {
                doc, err := yamlReader.Read()
                if err == io.EOF {
@@ -66,28 +68,22 @@ func (v *validator) validateFile(path string, 
dubboNamespace *string, defaultNam
        }
 }
 
-func (v *validator) validateResource(istioNamespace string, un 
*unstructured.Unstructured, writer io.Writer) (validation.Warnings, error) {
-       g := config.GroupVersionKind{
-               Group:   un.GroupVersionKind().Group,
-               Version: un.GroupVersionKind().Version,
-               Kind:    un.GroupVersionKind().Kind,
-       }
+func (v *validator) validateResource(dubboNamespace string, un 
*unstructured.Unstructured, writer io.Writer) (validation.Warning, error) {
        var errs error
        if errs != nil {
                return nil, errs
        }
-
        if un.GetAPIVersion() == 
operator.DubboOperatorGVK.GroupVersion().String() {
                if un.GetKind() == operator.DubboOperatorGVK.Kind {
                        if err := checkFields(un); err != nil {
                                return nil, err
                        }
-                       warnings, err := 
operatorvalidate.ParseAndValidateIstioOperator(un.Object, nil)
+                       warnings, err := 
operatorvalidate.ParseAndValidateDubboOperator(un.Object, nil)
                        if err != nil {
                                return nil, err
                        }
                        if len(warnings) > 0 {
-                               return validation.Warning(warnings.ToError()), 
nil
+                               return validation.Warning(warnings.ToErrors()), 
nil
                        }
                }
        }
@@ -105,6 +101,7 @@ func checkFields(un *unstructured.Unstructured) error {
 }
 
 func NewValidateCommand(ctx cli.Context) *cobra.Command {
+       var files []string
        vc := &cobra.Command{
                Use:   "validate -f FILENAME [options]",
                Short: "Validate Dubbo rules files",
@@ -115,11 +112,12 @@ func NewValidateCommand(ctx cli.Context) *cobra.Command {
   # Validate current services under 'default' namespace with in the cluster
   kubectl get services -o yaml | dubboctl validate -f -
 `,
-               Args:    cobra.NoArgs,
-               Aliases: []string{"v"},
+               Args:         cobra.NoArgs,
+               Aliases:      []string{"v"},
+               SilenceUsage: true,
                RunE: func(cmd *cobra.Command, _ []string) error {
                        dn := ctx.DubboNamespace()
-                       return validateFiles(&dn, nil, nil)
+                       return validateFiles(&dn, files, cmd.OutOrStderr())
                },
        }
        return vc
@@ -132,7 +130,7 @@ func validateFiles(dubboNamespace *string, files []string, 
writer io.Writer) err
        v := &validator{}
        var errs error
        var reader io.ReadCloser
-
+       warningsByFilename := map[string]validation.Warning{}
        processFile := func(path string) {
                var err error
                if path == "-" {
@@ -144,15 +142,114 @@ func validateFiles(dubboNamespace *string, files 
[]string, writer io.Writer) err
                                return
                        }
                }
-               warning, err := v.validateFile(path, istioNamespace, 
defaultNamespace, reader, writer)
+               warning, err := v.validateFile(path, dubboNamespace, reader, 
writer)
                if err != nil {
                        errs = multierror.Append(errs, err)
                }
                err = reader.Close()
                if err != nil {
-                       log.Infof("file: %s is not closed: %v", path, err)
+                       fmt.Printf("file: %s is not closed: %v", path, err)
                }
                warningsByFilename[path] = warning
        }
+       processDirectory := func(directory string, processFile func(string)) 
error {
+               err := filepath.Walk(directory, func(path string, info 
os.FileInfo, err error) error {
+                       if err != nil {
+                               return err
+                       }
+
+                       if info.IsDir() {
+                               return nil
+                       }
+
+                       if isFileFormatValid(path) {
+                               processFile(path)
+                       }
+
+                       return nil
+               })
+               return err
+       }
+
+       processedFiles := map[string]bool{}
+       for _, filename := range files {
+               var isDir bool
+               if filename != "-" {
+                       fi, err := os.Stat(filename)
+                       if err != nil {
+                               errs = multierror.Append(errs, 
fmt.Errorf("cannot stat file %q: %v", filename, err))
+                               continue
+                       }
+                       isDir = fi.IsDir()
+               }
+
+               if !isDir {
+                       processFile(filename)
+                       processedFiles[filename] = true
+                       continue
+               }
+               if err := processDirectory(filename, func(path string) {
+                       processFile(path)
+                       processedFiles[path] = true
+               }); err != nil {
+                       errs = multierror.Append(errs, err)
+               }
+       }
+       files = []string{}
+       for p := range processedFiles {
+               files = append(files, p)
+       }
+
+       if errs != nil {
+               for _, fname := range files {
+                       if w := warningsByFilename[fname]; w != nil {
+                               if fname == "-" {
+                                       _, _ = fmt.Fprint(writer, 
warningToString(w))
+                                       break
+                               }
+                               _, _ = fmt.Fprintf(writer, "%q has warnings: 
%v\n", fname, warningToString(w))
+                       }
+               }
+               return errs
+       }
+       for _, fname := range files {
+               if fname == "-" {
+                       if w := warningsByFilename[fname]; w != nil {
+                               _, _ = fmt.Fprint(writer, warningToString(w))
+                       } else {
+                               _, _ = fmt.Fprintf(writer, "validation 
succeed\n")
+                       }
+                       break
+               }
+
+               if w := warningsByFilename[fname]; w != nil {
+                       _, _ = fmt.Fprintf(writer, "%q has warnings: %v\n", 
fname, warningToString(w))
+               } else {
+                       _, _ = fmt.Fprintf(writer, "%q is valid\n", fname)
+               }
+       }
+
        return nil
 }
+
+func warningToString(w validation.Warning) string {
+       we, ok := w.(*multierror.Error)
+       if ok {
+               we.ErrorFormat = func(i []error) string {
+                       points := make([]string, len(i))
+                       for i, err := range i {
+                               points[i] = fmt.Sprintf("* %s", err)
+                       }
+
+                       return fmt.Sprintf(
+                               "\n\t%s\n",
+                               strings.Join(points, "\n\t"))
+               }
+       }
+       return w.Error()
+}
+
+func isFileFormatValid(file string) bool {
+       ext := filepath.Ext(file)
+       return slices.Contains(fileExtensions, ext)
+}
diff --git a/operator/pkg/apis/validation/validation.go 
b/operator/pkg/apis/validation/validation.go
new file mode 100644
index 00000000..3922a98c
--- /dev/null
+++ b/operator/pkg/apis/validation/validation.go
@@ -0,0 +1,62 @@
+package validation
+
+import (
+       "bytes"
+       "encoding/json"
+       "fmt"
+       "github.com/apache/dubbo-kubernetes/operator/pkg/apis"
+       "github.com/apache/dubbo-kubernetes/operator/pkg/util"
+       "github.com/apache/dubbo-kubernetes/operator/pkg/values"
+       "github.com/apache/dubbo-kubernetes/pkg/kube"
+       "sigs.k8s.io/yaml"
+)
+
+type Warnings = util.Errors
+
+func ParseAndValidateDubboOperator(dopm values.Map, client kube.CLIClient) 
(Warnings, util.Errors) {
+       iop := &apis.DubboOperator{}
+       dec := json.NewDecoder(bytes.NewBufferString(dopm.JSON()))
+       dec.DisallowUnknownFields()
+       if err := dec.Decode(iop); err != nil {
+               return nil, util.NewErrs(fmt.Errorf("could not unmarshal: %v", 
err))
+       }
+       var warnings Warnings
+       var errors util.Errors
+
+       vw, ve := validateValues(iop)
+       warnings = util.AppendErrs(warnings, vw)
+       errors = util.AppendErrs(errors, ve)
+       errors = util.AppendErr(errors, 
validateComponentNames(iop.Spec.Components))
+       return warnings, errors
+}
+
+type FeatureValidator func(*apis.Values, apis.DubboOperatorSpec) (Warnings, 
util.Errors)
+
+func validateFeatures(values *apis.Values, spec apis.DubboOperatorSpec) 
(Warnings, util.Errors) {
+       validators := []FeatureValidator{}
+       var warnings Warnings
+       var errs util.Errors
+       for _, validator := range validators {
+               newWarnings, newErrs := validator(values, spec)
+               errs = util.AppendErrs(errs, newErrs)
+               warnings = append(warnings, newWarnings...)
+       }
+       return warnings, errs
+}
+
+func validateValues(raw *apis.DubboOperator) (Warnings, util.Errors) {
+       values := &apis.Values{}
+       if err := yaml.Unmarshal(raw.Spec.Values, values); err != nil {
+               return nil, util.NewErrs(fmt.Errorf("could not unmarshal: %v", 
err))
+       }
+       warnings, errs := validateFeatures(values, raw.Spec)
+
+       return warnings, errs
+}
+
+func validateComponentNames(components *apis.DubboComponentSpec) error {
+       if components == nil {
+               return nil
+       }
+       return nil
+}

Reply via email to