Skip to content

Commit

Permalink
added json output support
Browse files Browse the repository at this point in the history
  • Loading branch information
pghildiyal committed Jul 10, 2023
1 parent 2b36d51 commit f3030b3
Show file tree
Hide file tree
Showing 15 changed files with 480 additions and 215 deletions.
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ require (
github.com/spf13/jwalterweatherman v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tidwall/gjson v1.8.1 // indirect
github.com/tidwall/match v1.0.3 // indirect
github.com/tidwall/pretty v1.1.0 // indirect
github.com/tidwall/gjson v1.9.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -357,12 +357,14 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/gjson v1.8.1 h1:8j5EE9Hrh3l9Od1OIEDAb7IpezNA20UdRngNAj5N0WU=
github.com/tidwall/gjson v1.8.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
github.com/tidwall/gjson v1.9.3 h1:hqzS9wAHMO+KVBBkLxYdkEeeFHuqr95GfClRLKlgK0E=
github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.1.7 h1:sgVPwu/yygHJ2m1pJDLgGM/h+1F5odx5Q9ljG3imRm8=
github.com/tidwall/sjson v1.1.7/go.mod h1:w/yG+ezBeTdUxiKs5NcPicO9diP38nk96QBAbIIGeFs=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
Expand Down
6 changes: 3 additions & 3 deletions kubedd/kubedd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
func TestValidateCluster(t *testing.T) {
cluster := pkg.NewCluster("", "")
config := pkg.NewDefaultConfig()
config.SelectKinds = []string{"Deployment"}
config.SelectNamespaces = []string{"esrgan2k"}
config.TargetKubernetesVersion = "1.27"
config.SelectKinds = []string{"ReplicaSet"}
//config.SelectNamespaces = []string{"esrgan2k"}
config.TargetKubernetesVersion = "1.24"
//config.SelectNamespaces = []string{"prod"}
type args struct {
cluster *pkg.Cluster
Expand Down
3 changes: 2 additions & 1 deletion pkg/Cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func NewCluster(kubeconfig string, kubecontext string) *Cluster {

clientConfig := clientcmd.NewDefaultClientConfig(*config, &configOverrides)
cluster.restConfig, err = clientConfig.ClientConfig()
cluster.restConfig.WarningHandler = rest.NoWarnings{}
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -86,7 +87,7 @@ func (c *Cluster) FetchK8sObjects(gvks []schema.GroupVersionKind, conf *Config)
resources = append(resources, gvr.Resource)
}
for _, resource := range resources {
if strings.Contains(resource.Resource, "lists") || strings.Contains(resource.Resource, "reviews") || strings.EqualFold(resource.Resource, "bindings") {
if strings.Contains(resource.Resource, "lists") || strings.Contains(resource.Resource, "reviews") || strings.EqualFold(resource.Resource, "bindings") {
continue
}
resInf := c.clientset.Resource(resource)
Expand Down
17 changes: 9 additions & 8 deletions pkg/Config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
package pkg

import (
"fmt"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -83,22 +84,22 @@ type Config struct {
IgnoreKeysFromDeprecation []string

// IgnoreKeysFromValidation is the list of keys to be skipped for validation check
IgnoreKeysFromValidation []string
IgnoreKeysFromValidation []string

// SelectNamespaces is the list of namespaces to be validated, by default all namespaces are validated
SelectNamespaces []string
SelectNamespaces []string

// IgnoreNamespaces is the list of namespaces to be skipped for validation, by default none are skipped
IgnoreNamespaces []string
IgnoreNamespaces []string

// SelectKinds is the list of kinds to be validated, by default all kinds are validated
SelectKinds []string
SelectKinds []string

// IgnoreKinds is the list of kinds to be skipped for validation, by default none are skipped
IgnoreKinds []string
IgnoreKinds []string

// IgnoreNullErrors is the flag to ignore null value errors
IgnoreNullErrors bool
IgnoreNullErrors bool
}

// NewDefaultConfig creates a Config with default values
Expand All @@ -117,12 +118,12 @@ func AddKubeaddFlags(cmd *cobra.Command, config *Config) *cobra.Command {
cmd.Flags().StringVarP(&config.SourceSchemaLocation, "source-schema-location", "", "", "SourceSchemaLocation is the file path of kubernetes versions of the cluster on which manifests are deployed. Use this in air-gapped environment where it internet access is unavailable.")
cmd.Flags().StringVarP(&config.TargetKubernetesVersion, "target-kubernetes-version", "", "1.22", "Version of Kubernetes to migrate to eg 1.22, 1.21, 1.12")
cmd.Flags().StringVarP(&config.SourceKubernetesVersion, "source-kubernetes-version", "", "", "Version of Kubernetes of the cluster on which kubernetes objects are deployed currently, ignored in case cluster is provided. In case of directory defaults to same as target-kubernetes-version.")
//cmd.Flags().StringVarP(&config.OutputFormat, "output", "o", "", fmt.Sprintf("The format of the output of this script. Options are: %v", "stdOut"))
cmd.Flags().StringVarP(&config.OutputFormat, "output", "o", "", fmt.Sprintf("The format of the output of this script. Options are: %v", "(stdOut | json)"))
//cmd.Flags().BoolVar(&config.Quiet, "quiet", false, "Silences any output aside from the direct results")
cmd.Flags().BoolVar(&config.InsecureSkipTLSVerify, "insecure-skip-tls-verify", false, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure")
cmd.Flags().StringSliceVarP(&config.SelectNamespaces, "select-namespaces", "", []string{}, "A comma-separated list of namespaces to be selected, if left empty all namespaces are selected")
cmd.Flags().StringSliceVarP(&config.IgnoreNamespaces, "ignore-namespaces", "", []string{"kube-system"}, "A comma-separated list of namespaces to be skipped")
cmd.Flags().StringSliceVarP(&config.IgnoreKinds, "ignore-kinds", "", []string{"event","CustomResourceDefinition"}, "A comma-separated list of kinds to be skipped")
cmd.Flags().StringSliceVarP(&config.IgnoreKinds, "ignore-kinds", "", []string{"event", "CustomResourceDefinition"}, "A comma-separated list of kinds to be skipped")
cmd.Flags().StringSliceVarP(&config.SelectKinds, "select-kinds", "", []string{}, "A comma-separated list of kinds to be selected, if left empty all kinds are selected")
cmd.Flags().StringSliceVarP(&config.IgnoreKeysFromDeprecation, "ignore-keys-for-deprecation", "", []string{"metadata*", "status*"}, "A comma-separated list of keys to be ignored for depreciation check")
cmd.Flags().StringSliceVarP(&config.IgnoreKeysFromValidation, "ignore-keys-for-validation", "", []string{"status*", "metadata*"}, "A comma-separated list of keys to be ignored for validation check")
Expand Down
132 changes: 114 additions & 18 deletions pkg/Output.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ func (s *STDOutputManager) PutBulk(results []ValidationResult) error {
continue
} else if result.Deleted {
deleted = append(deleted, result)
} else if result.Deprecated && len(result.LatestAPIVersion) > 0 {
deprecated = append(deprecated, result)
/*} else if result.Deprecated && len(result.LatestAPIVersion) > 0 {
deprecated = append(deprecated, result)*/
} else if result.Deprecated {
//Skip
deprecated = append(deprecated, result)
} else if len(result.LatestAPIVersion) > 0 {
newerVersion = append(newerVersion, result)
} else {
if len(result.ErrorsForOriginal) > 0 || len(result.ErrorsForLatest) > 0 ||
len(result.DeprecationForOriginal) > 0 || len(result.DeprecationForLatest) > 0 {
if len(result.ErrorsForOriginal) == 0 && len(result.ErrorsForLatest) == 0 &&
len(result.DeprecationForOriginal) == 0 && len(result.DeprecationForLatest) == 0 {
unchanged = append(unchanged, result)
}
}
Expand Down Expand Up @@ -137,7 +137,7 @@ func (s *STDOutputManager) PutBulk(results []ValidationResult) error {
fmt.Printf("%s\n", yellow(">>>> Deprecated API Version's <<<<"))
s.SummaryTableBodyOutput(deprecated)
fmt.Println("")
//s.DeprecationTableBodyOutput(results, true)
s.DeprecationTableBodyOutput(deprecated, true)
s.ValidationErrorTableBodyOutput(deprecated, true)
s.DeprecationTableBodyOutput(deprecated, false)
s.ValidationErrorTableBodyOutput(deprecated, false)
Expand Down Expand Up @@ -319,7 +319,7 @@ type dataEvalResult struct {
type jsonOutputManager struct {
logger *log.Logger

data []dataEvalResult
data []SummaryValidationResult
}

func newDefaultJSONOutputManager() *jsonOutputManager {
Expand Down Expand Up @@ -348,25 +348,121 @@ func getStatus(r ValidationResult) status {
return statusValid
}

func (j *jsonOutputManager) PutBulk(r []ValidationResult) error {
func (j *jsonOutputManager) PutBulk(vrs []ValidationResult) error {
svrs := make([]SummaryValidationResult, len(vrs))
for _, vr := range vrs {
if vr.Deleted == false && vr.Deprecated == false && len(vr.ErrorsForLatest) == 0 && len(vr.ErrorsForOriginal) == 0 && len(vr.DeprecationForLatest) == 0 && len(vr.DeprecationForOriginal) == 0 {
continue
}
svr := SummaryValidationResult{
Deleted: vr.Deleted,
Deprecated: vr.Deprecated,
Kind: vr.Kind,
ResourceName: vr.ResourceName,
APIVersion: vr.APIVersion,
FileName: vr.FileName,
IsVersionSupported: vr.IsVersionSupported,
LatestAPIVersion: vr.LatestAPIVersion,
}
for _, se := range vr.ErrorsForOriginal {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.ErrorsForOriginal = append(svr.ErrorsForOriginal, sse)
}
for _, se := range vr.ErrorsForLatest {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.ErrorsForLatest = append(svr.ErrorsForLatest, sse)
}
for _, se := range vr.DeprecationForOriginal {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.DeprecationForOriginal = append(svr.DeprecationForOriginal, sse)
}
for _, se := range vr.DeprecationForLatest {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.DeprecationForLatest = append(svr.DeprecationForLatest, sse)
}
svrs = append(svrs, svr)
}
j.data = svrs
return nil
}

func (j *jsonOutputManager) Put(r ValidationResult) error {
func (j *jsonOutputManager) Put(vr ValidationResult) error {
// stringify gojsonschema errors
// use a pre-allocated slice to ensure the json will have an
// empty array in the "zero" case
errs := make([]string, 0, len(r.Errors))
for _, e := range r.Errors {
errs = append(errs, e.String())
//errs := make([]string, 0, len(r.Errors))
//for _, e := range r.Errors {
// errs = append(errs, e.String())
//}

svr := SummaryValidationResult{
Deleted: vr.Deleted,
Deprecated: vr.Deprecated,
Kind: vr.Kind,
ResourceName: vr.ResourceName,
APIVersion: vr.APIVersion,
FileName: vr.FileName,
IsVersionSupported: vr.IsVersionSupported,
LatestAPIVersion: vr.LatestAPIVersion,
}
for _, se := range vr.ErrorsForOriginal {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.ErrorsForOriginal = append(svr.ErrorsForOriginal, sse)
}
for _, se := range vr.ErrorsForLatest {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.ErrorsForLatest = append(svr.ErrorsForLatest, sse)
}
for _, se := range vr.DeprecationForOriginal {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.DeprecationForOriginal = append(svr.DeprecationForOriginal, sse)
}
for _, se := range vr.DeprecationForLatest {
sse := &SummarySchemaError{
Path: strings.Join(se.JSONPointer(), "/"),
SchemaField: se.SchemaField,
Reason: se.Reason,
Origin: se.Origin,
}
svr.DeprecationForLatest = append(svr.DeprecationForLatest, sse)
}

j.data = append(j.data, dataEvalResult{
Filename: r.FileName,
Kind: r.Kind,
Status: getStatus(r),
Errors: errs,
})
j.data = append(j.data, svr)

return nil
}
Expand Down
23 changes: 23 additions & 0 deletions pkg/Types.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,29 @@ type ValidationResult struct {
IsVersionSupported int
}

type SummarySchemaError struct {
Path string
SchemaField string
Reason string
Origin error
}

type SummaryValidationResult struct {
FileName string
Kind string
APIVersion string
ResourceName string
ResourceNamespace string
Deleted bool
Deprecated bool
LatestAPIVersion string
IsVersionSupported int
ErrorsForOriginal []*SummarySchemaError
ErrorsForLatest []*SummarySchemaError
DeprecationForOriginal []*SummarySchemaError
DeprecationForLatest []*SummarySchemaError
}

// VersionKind returns a string representation of this result's apiVersion and kind
func (v *ValidationResult) VersionKind() string {
return v.APIVersion + "/" + v.Kind
Expand Down
13 changes: 8 additions & 5 deletions pkg/Validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import (
"strings"
)



type kubeSpec struct {
*openapi3.T
kindInfoMap map[string][]*KindInfo
Expand Down Expand Up @@ -89,8 +87,13 @@ func (ks *kubeSpec) ValidateObject(object map[string]interface{}) (ValidationRes
validationResult.ErrorsForOriginal = ves
validationResult.DeprecationForOriginal = des
validationResult.Deprecated = deprecated
} else if len(original) == 0 && len(latest) > 0 {
//if original == latest {
// validationResult.ErrorsForLatest = ves
// validationResult.DeprecationForLatest = des
//}
} else if len(latest) == 0 {
validationResult.Deleted = true
validationResult.IsVersionSupported = 2
}
if len(latest) > 0 && original != latest {
var ves []*openapi3.SchemaError
Expand Down Expand Up @@ -255,7 +258,7 @@ func (ks *kubeSpec) applySchema(object map[string]interface{}, token string) (op

opts := []openapi3.SchemaValidationOption{openapi3.MultiErrors()}
depError := VisitJSON(scm, object, SchemaSettings{MultiError: true})
if len(depError) > 0 {
if strings.Index(strings.ToLower(scm.Description), "deprecated") >= 0 {
deprecated = true
}
validationError = append(validationError, depError...)
Expand All @@ -282,7 +285,7 @@ func (ks *kubeSpec) getKeyForGVFromToken(token string) (string, error) {
}

func (ks *kubeSpec) schemaLookup(token string) (*openapi3.Schema, error) {
for ; ; {
for {
if strings.Index(token, "/") > 0 {
parts := strings.Split(token, "/")
token = parts[len(parts)-1]
Expand Down
11 changes: 6 additions & 5 deletions vendor/github.com/tidwall/gjson/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions vendor/github.com/tidwall/gjson/SYNTAX.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit f3030b3

Please sign in to comment.