Skip to content

Commit f1fce6d

Browse files
committed
crd-tools: add command to remove descriptions
This is super-handy for diffing CRDs
1 parent b160be8 commit f1fce6d

File tree

3 files changed

+142
-9
lines changed

3 files changed

+142
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package removedescriptions
16+
17+
import (
18+
"context"
19+
"errors"
20+
"fmt"
21+
22+
"github.com/GoogleCloudPlatform/k8s-config-connector/scripts/crd-tools/pkg/objectvisitor"
23+
"github.com/spf13/cobra"
24+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
25+
)
26+
27+
func AddCommand(parent *cobra.Command) {
28+
var opt Options
29+
cmd := &cobra.Command{
30+
Use: "remove-descriptions",
31+
Short: "Remove descriptions in objects, for easier schema-only comparison.",
32+
RunE: func(cmd *cobra.Command, args []string) error {
33+
return Run(cmd.Context(), opt)
34+
},
35+
}
36+
cmd.Flags().StringVar(&opt.Dir, "dir", "", "Directory to process")
37+
parent.AddCommand(cmd)
38+
}
39+
40+
type Options struct {
41+
Dir string
42+
}
43+
44+
func (o *Options) Validate() error {
45+
return nil
46+
}
47+
48+
func (o *Options) InitDefaults() {
49+
o.Dir = ""
50+
}
51+
52+
type visitor struct {
53+
errors []error
54+
}
55+
56+
func (v *visitor) VisitObject(crd *unstructured.Unstructured) error {
57+
v.visitMap(crd.Object)
58+
return errors.Join(v.errors...)
59+
}
60+
61+
func (v *visitor) visitMap(m map[string]any) map[string]any {
62+
delete(m, "description")
63+
for k, val := range m {
64+
m[k] = v.visitAny(val)
65+
}
66+
return m
67+
}
68+
69+
func (v *visitor) visitSlice(s []any) []any {
70+
for i, val := range s {
71+
s[i] = v.visitAny(val)
72+
}
73+
return s
74+
}
75+
76+
func (v *visitor) visitAny(val any) any {
77+
switch val := val.(type) {
78+
case map[string]any:
79+
return v.visitMap(val)
80+
case []any:
81+
return v.visitSlice(val)
82+
case string:
83+
return val
84+
case bool, int32, int64, float32, float64:
85+
return val
86+
case nil:
87+
return val
88+
default:
89+
v.errors = append(v.errors, fmt.Errorf("unexpected type %T", val))
90+
return val
91+
}
92+
}
93+
94+
func Run(ctx context.Context, options Options) error {
95+
if err := options.Validate(); err != nil {
96+
return err
97+
}
98+
99+
visitor := &visitor{}
100+
if options.Dir != "" {
101+
return objectvisitor.VisitObjectsInDirectory(ctx, options.Dir, visitor)
102+
} else {
103+
return objectvisitor.VisitObjectsFromStdin(ctx, visitor)
104+
}
105+
}

scripts/crd-tools/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/GoogleCloudPlatform/k8s-config-connector/scripts/crd-tools/cmd/deleteannotation"
2626
"github.com/GoogleCloudPlatform/k8s-config-connector/scripts/crd-tools/cmd/deletefield"
2727
"github.com/GoogleCloudPlatform/k8s-config-connector/scripts/crd-tools/cmd/reflowdescriptions"
28+
"github.com/GoogleCloudPlatform/k8s-config-connector/scripts/crd-tools/cmd/removedescriptions"
2829
"github.com/GoogleCloudPlatform/k8s-config-connector/scripts/crd-tools/cmd/setannotation"
2930
"github.com/GoogleCloudPlatform/k8s-config-connector/scripts/crd-tools/cmd/setfield"
3031
"github.com/spf13/cobra"
@@ -46,6 +47,7 @@ func run(ctx context.Context) error {
4647
deleteannotation.AddCommand(rootCmd)
4748
deletefield.AddCommand(rootCmd)
4849
reflowdescriptions.AddCommand(rootCmd)
50+
removedescriptions.AddCommand(rootCmd)
4951
setannotation.AddCommand(rootCmd)
5052
setfield.AddCommand(rootCmd)
5153

scripts/crd-tools/pkg/objectvisitor/objectvisitor.go

+35-9
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"bytes"
1919
"context"
2020
"fmt"
21+
"io"
2122
"io/fs"
2223
"os"
2324
"path/filepath"
@@ -56,12 +57,41 @@ func VisitObjectsInDirectory(ctx context.Context, dir string, visitor Visitor) e
5657
return nil
5758
}
5859

60+
func VisitObjectsFromStdin(ctx context.Context, visitor Visitor) error {
61+
b, err := io.ReadAll(os.Stdin)
62+
if err != nil {
63+
return fmt.Errorf("reading from stdin: %w", err)
64+
}
65+
out, err := processBytes(ctx, b, visitor)
66+
if err != nil {
67+
return fmt.Errorf("processing stdin: %w", err)
68+
}
69+
70+
if _, err := os.Stdout.Write(out); err != nil {
71+
return fmt.Errorf("error writing to stdout: %w", err)
72+
}
73+
74+
return nil
75+
}
76+
5977
func processFile(ctx context.Context, p string, visitor Visitor) error {
6078
b, err := os.ReadFile(p)
6179
if err != nil {
6280
return fmt.Errorf("error reading file %q: %w", p, err)
6381
}
6482

83+
out, err := processBytes(ctx, b, visitor)
84+
if err != nil {
85+
return fmt.Errorf("processing file %q: %w", p, err)
86+
}
87+
88+
if err := os.WriteFile(p, out, 0644); err != nil {
89+
return fmt.Errorf("error writing file %q: %w", p, err)
90+
}
91+
return nil
92+
}
93+
94+
func processBytes(ctx context.Context, b []byte, visitor Visitor) ([]byte, error) {
6595
var out bytes.Buffer
6696

6797
// We preserve the yaml header (copyright, typically)
@@ -76,38 +106,34 @@ func processFile(ctx context.Context, p string, visitor Visitor) error {
76106

77107
yamls, err := kccyaml.SplitYAML(b)
78108
if err != nil {
79-
return fmt.Errorf("error splitting bytes into YAMLs: %w", err)
109+
return nil, fmt.Errorf("error splitting bytes into YAMLs: %w", err)
80110
}
81111
objects := make([]*unstructured.Unstructured, 0)
82112
for _, y := range yamls {
83113
crd := &unstructured.Unstructured{}
84114
if err := yaml.Unmarshal(y, crd); err != nil {
85-
return fmt.Errorf("error unmarshalling bytes to object: %w", err)
115+
return nil, fmt.Errorf("error unmarshalling bytes to object: %w", err)
86116
}
87117
objects = append(objects, crd)
88118
}
89119

90120
for _, obj := range objects {
91121
if err := visitor.VisitObject(obj); err != nil {
92-
return err
122+
return nil, err
93123
}
94124
}
95125

96126
for i, obj := range objects {
97127
b, err := yaml.Marshal(obj)
98128
if err != nil {
99-
return fmt.Errorf("error marshalling object to bytes: %w", err)
129+
return nil, fmt.Errorf("error marshalling object to bytes: %w", err)
100130
}
101131
out.Write(b)
102132
if i != 0 {
103133
out.WriteString("\n---\n")
104134
}
105135
}
106-
107-
if err := os.WriteFile(p, out.Bytes(), 0644); err != nil {
108-
return fmt.Errorf("error writing file %q: %w", p, err)
109-
}
110-
return nil
136+
return out.Bytes(), nil
111137
}
112138

113139
func PtrTo[T any](t T) *T {

0 commit comments

Comments
 (0)