Skip to content

Commit 3bb4d32

Browse files
Joeavaikathclaude
andcommitted
Fix nonadmin get commands with -o yaml/json output
Velero's output.PrintWithFormat uses an upstream scheme that doesn't include NonAdmin CRD types, causing "no kind is registered" errors when using -o yaml or -o json flags. Implemented custom output package with NonAdmin scheme registration to enable proper serialization of downstream types. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Joseph <jvaikath@redhat.com>
1 parent c955f49 commit 3bb4d32

6 files changed

Lines changed: 665 additions & 3 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,5 @@ go.work
4848
.Trashes
4949
ehthumbs.db
5050
Thumbs.db
51+
52+
vendor/

cmd/non-admin/backup/get.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ import (
2020
"fmt"
2121
"time"
2222

23+
"github.com/migtools/oadp-cli/cmd/non-admin/output"
2324
"github.com/migtools/oadp-cli/cmd/shared"
2425
nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1"
2526
"github.com/spf13/cobra"
2627
"github.com/vmware-tanzu/velero/pkg/client"
27-
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
2828
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
2929
)
3030

cmd/non-admin/bsl/get.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import (
2121
"fmt"
2222
"time"
2323

24+
"github.com/migtools/oadp-cli/cmd/non-admin/output"
2425
"github.com/migtools/oadp-cli/cmd/shared"
2526
nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1"
2627
"github.com/spf13/cobra"
2728
"github.com/vmware-tanzu/velero/pkg/client"
28-
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
2929
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
3030
)
3131

cmd/non-admin/output/output.go

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
Copyright 2025 The OADP CLI Contributors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package output
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"io"
23+
24+
nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1"
25+
"github.com/pkg/errors"
26+
"github.com/spf13/cobra"
27+
"github.com/spf13/pflag"
28+
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
29+
velerooutput "github.com/vmware-tanzu/velero/pkg/cmd/util/output"
30+
"k8s.io/apimachinery/pkg/api/meta"
31+
"k8s.io/apimachinery/pkg/runtime"
32+
"k8s.io/apimachinery/pkg/runtime/serializer"
33+
)
34+
35+
// NonAdminScheme returns a runtime.Scheme with NonAdmin types registered
36+
func NonAdminScheme() *runtime.Scheme {
37+
scheme := runtime.NewScheme()
38+
39+
// Add NonAdmin types
40+
if err := nacv1alpha1.AddToScheme(scheme); err != nil {
41+
panic(fmt.Sprintf("failed to add NonAdmin types to scheme: %v", err))
42+
}
43+
44+
// Add Velero types for compatibility
45+
if err := velerov1api.AddToScheme(scheme); err != nil {
46+
panic(fmt.Sprintf("failed to add Velero types to scheme: %v", err))
47+
}
48+
49+
return scheme
50+
}
51+
52+
// BindFlags wraps Velero's BindFlags to add output flags
53+
func BindFlags(flags *pflag.FlagSet) {
54+
velerooutput.BindFlags(flags)
55+
}
56+
57+
// ClearOutputFlagDefault wraps Velero's ClearOutputFlagDefault
58+
func ClearOutputFlagDefault(cmd *cobra.Command) {
59+
velerooutput.ClearOutputFlagDefault(cmd)
60+
}
61+
62+
// PrintWithFormat prints the provided object in the format specified by
63+
// the command's flags. This is a custom implementation for nonadmin commands
64+
// that supports NonAdmin CRD types (NonAdminBackup, NonAdminRestore, etc.)
65+
func PrintWithFormat(c *cobra.Command, obj runtime.Object) (bool, error) {
66+
format := velerooutput.GetOutputFlagValue(c)
67+
if format == "" {
68+
return false, nil
69+
}
70+
71+
switch format {
72+
case "json", "yaml":
73+
return printEncoded(obj, format)
74+
case "table":
75+
// Table format is not supported by this function
76+
// The caller should handle table printing
77+
return false, nil
78+
}
79+
80+
return false, errors.Errorf("unsupported output format %q; valid values are 'table', 'json', and 'yaml'", format)
81+
}
82+
83+
func printEncoded(obj runtime.Object, format string) (bool, error) {
84+
// assume we're printing obj
85+
toPrint := obj
86+
87+
if meta.IsListType(obj) {
88+
list, _ := meta.ExtractList(obj)
89+
if len(list) == 1 {
90+
// if obj was a list and there was only 1 item, just print that 1 instead of a list
91+
toPrint = list[0]
92+
}
93+
}
94+
95+
encoded, err := encode(toPrint, format)
96+
if err != nil {
97+
return false, err
98+
}
99+
100+
fmt.Println(string(encoded))
101+
102+
return true, nil
103+
}
104+
105+
func encode(obj runtime.Object, format string) ([]byte, error) {
106+
buf := new(bytes.Buffer)
107+
108+
if err := encodeTo(obj, format, buf); err != nil {
109+
return nil, err
110+
}
111+
return buf.Bytes(), nil
112+
}
113+
114+
func encodeTo(obj runtime.Object, format string, w io.Writer) error {
115+
encoder, err := encoderFor(format, obj)
116+
if err != nil {
117+
return err
118+
}
119+
120+
return errors.WithStack(encoder.Encode(obj, w))
121+
}
122+
123+
func encoderFor(format string, obj runtime.Object) (runtime.Encoder, error) {
124+
var encoder runtime.Encoder
125+
126+
// Use NonAdminScheme instead of Velero's scheme
127+
codecFactory := serializer.NewCodecFactory(NonAdminScheme())
128+
129+
desiredMediaType := fmt.Sprintf("application/%s", format)
130+
serializerInfo, found := runtime.SerializerInfoForMediaType(codecFactory.SupportedMediaTypes(), desiredMediaType)
131+
if !found {
132+
return nil, errors.Errorf("unable to locate an encoder for %q", desiredMediaType)
133+
}
134+
if serializerInfo.PrettySerializer != nil {
135+
encoder = serializerInfo.PrettySerializer
136+
} else {
137+
encoder = serializerInfo.Serializer
138+
}
139+
140+
if !obj.GetObjectKind().GroupVersionKind().Empty() {
141+
return encoder, nil
142+
}
143+
144+
// Use the appropriate GroupVersion for encoding
145+
// For NonAdmin types, use nacv1alpha1.GroupVersion
146+
encoder = codecFactory.EncoderForVersion(encoder, nacv1alpha1.GroupVersion)
147+
return encoder, nil
148+
}

0 commit comments

Comments
 (0)