Skip to content

Commit 02db11c

Browse files
committed
Refactor non-admin backup storage location commands
- Introduced new subcommands for managing backup storage location requests: approve, deny, describe, and get. - Updated the create command to enhance its functionality and examples. - Improved help text for commands to provide clearer guidance on usage. - Added tests to ensure help messages are accurate and comprehensive. - Enhanced the overall structure and organization of the non-admin command set.
1 parent 86623d6 commit 02db11c

9 files changed

Lines changed: 960 additions & 54 deletions

File tree

cmd/non-admin/bsl/bsl.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ import (
2525
func NewBSLCommand(f client.Factory) *cobra.Command {
2626
c := &cobra.Command{
2727
Use: "bsl",
28-
Short: "Work with non-admin backup storage locations",
29-
Long: "Work with non-admin backup storage locations",
28+
Short: "Create and manage backup storage locations",
29+
Long: "Create and manage non-admin backup storage locations and their approval requests",
3030
}
3131

3232
c.AddCommand(
33-
NewCreateCommand(f, "create"),
33+
NewCreateCommand(f),
34+
NewRequestCommand(f),
3435
)
3536

3637
return c

cmd/non-admin/bsl/create.go

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -30,32 +30,44 @@ import (
3030
"github.com/vmware-tanzu/velero/pkg/client"
3131
"github.com/vmware-tanzu/velero/pkg/cmd"
3232
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
33+
corev1 "k8s.io/api/core/v1"
3334
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3435
)
3536

36-
func NewCreateCommand(f client.Factory, use string) *cobra.Command {
37+
func NewCreateCommand(f client.Factory) *cobra.Command {
3738
o := NewCreateOptions()
3839

3940
c := &cobra.Command{
40-
Use: use + " NAME",
41+
Use: "create NAME",
4142
Short: "Create a non-admin backup storage location",
4243
Args: cobra.ExactArgs(1),
4344
Run: func(c *cobra.Command, args []string) {
4445
cmd.CheckError(o.Complete(args, f))
4546
cmd.CheckError(o.Validate(c, args, f))
4647
cmd.CheckError(o.Run(c, f))
4748
},
48-
Example: ` # Create a non-admin backup storage location
49-
kubectl oadp nonadmin bsl create my-bsl --backup-storage-location default
50-
51-
# Create a non-admin backup storage location with specific namespace
52-
kubectl oadp nonadmin bsl create my-bsl --backup-storage-location aws-bsl --namespace my-namespace
53-
54-
# Create with custom BSL namespace (if OADP operator is not in openshift-adp)
55-
kubectl oadp nonadmin bsl create my-bsl --backup-storage-location default --bsl-namespace velero
56-
57-
# View the YAML for a non-admin backup storage location without sending it to the server
58-
kubectl oadp nonadmin bsl create my-bsl --backup-storage-location default -o yaml`,
49+
Example: ` # Create a non-admin backup storage location for AWS
50+
kubectl oadp nonadmin bsl create my-storage \
51+
--provider aws \
52+
--bucket my-velero-bucket \
53+
--credential-name cloud-credentials \
54+
--region us-east-1
55+
56+
# Create with prefix for organizing backups
57+
kubectl oadp nonadmin bsl create my-storage \
58+
--provider aws \
59+
--bucket my-velero-bucket \
60+
--prefix velero-backups \
61+
--credential-name cloud-credentials \
62+
--region us-east-1
63+
64+
# View the YAML without creating the resource
65+
kubectl oadp nonadmin bsl create my-storage \
66+
--provider aws \
67+
--bucket my-bucket \
68+
--credential-name cloud-credentials \
69+
--region us-east-1 \
70+
-o yaml`,
5971
}
6072

6173
o.BindFlags(c.Flags())
@@ -66,89 +78,115 @@ func NewCreateCommand(f client.Factory, use string) *cobra.Command {
6678
}
6779

6880
type CreateOptions struct {
69-
Name string
70-
BackupStorageLocation string
71-
NonAdminNamespace string
72-
BSLNamespace string
73-
client kbclient.WithWatch
81+
Name string
82+
Namespace string
83+
Provider string
84+
Bucket string
85+
Prefix string
86+
CredentialName string
87+
Region string
88+
Config map[string]string
89+
client kbclient.WithWatch
7490
}
7591

7692
func NewCreateOptions() *CreateOptions {
7793
return &CreateOptions{
78-
BSLNamespace: "openshift-adp", // Default OADP operator namespace
94+
Config: make(map[string]string),
7995
}
8096
}
8197

8298
func (o *CreateOptions) BindFlags(flags *pflag.FlagSet) {
83-
flags.StringVar(&o.BackupStorageLocation, "backup-storage-location", "", "Name of the BackupStorageLocation to reference.")
84-
flags.StringVar(&o.NonAdminNamespace, "namespace", "", "Namespace for the NonAdminBackupStorageLocation (defaults to current context namespace).")
85-
flags.StringVar(&o.BSLNamespace, "bsl-namespace", "openshift-adp", "Namespace where the BackupStorageLocation exists.")
99+
flags.StringVar(&o.Provider, "provider", "", "Storage provider (required). Examples: aws, azure, gcp")
100+
flags.StringVar(&o.Bucket, "bucket", "", "Object storage bucket name (required)")
101+
flags.StringVar(&o.Prefix, "prefix", "", "Prefix for backup objects in the bucket")
102+
flags.StringVar(&o.CredentialName, "credential-name", "", "Name of the credential secret (required)")
103+
flags.StringVar(&o.Region, "region", "", "Storage region (required for some providers like AWS)")
104+
flags.StringToStringVar(&o.Config, "config", nil, "Additional provider-specific configuration (key=value pairs)")
86105
}
87106

88107
func (o *CreateOptions) Complete(args []string, f client.Factory) error {
89108
o.Name = args[0]
90109

91-
// Create client with Velero scheme for BackupStorageLocation access
92-
client, err := shared.NewClientWithScheme(f, shared.ClientOptions{
93-
IncludeVeleroTypes: true,
94-
})
110+
// Create client with full scheme including NonAdmin and Velero types
111+
client, err := shared.NewClientWithFullScheme(f)
95112
if err != nil {
96113
return err
97114
}
98115

99116
o.client = client
100117

101-
if o.NonAdminNamespace == "" {
102-
namespace := f.Namespace()
103-
o.NonAdminNamespace = namespace
118+
// Get the current namespace
119+
currentNS, err := shared.GetCurrentNamespace()
120+
if err != nil {
121+
return fmt.Errorf("failed to determine current namespace: %w", err)
104122
}
123+
o.Namespace = currentNS
105124

106125
return nil
107126
}
108127

109128
func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Factory) error {
110-
if o.BackupStorageLocation == "" {
111-
return fmt.Errorf("--backup-storage-location is required")
129+
if o.Provider == "" {
130+
return fmt.Errorf("--provider is required")
131+
}
132+
if o.Bucket == "" {
133+
return fmt.Errorf("--bucket is required")
134+
}
135+
if o.CredentialName == "" {
136+
return fmt.Errorf("--credential-name is required")
112137
}
113138

114139
return nil
115140
}
116141

117142
func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
118-
// If we have a BackupStorageLocation name, we need to fetch its spec
119-
var bslSpec *velerov1.BackupStorageLocationSpec
120-
if o.BackupStorageLocation != "" {
121-
// Get the existing BackupStorageLocation to copy its spec
122-
existingBSL := &velerov1.BackupStorageLocation{}
123-
err := o.client.Get(context.Background(), kbclient.ObjectKey{
124-
Name: o.BackupStorageLocation,
125-
Namespace: o.BSLNamespace, // Use the BSLNamespace flag
126-
}, existingBSL)
127-
if err != nil {
128-
return fmt.Errorf("failed to get BackupStorageLocation %q: %w", o.BackupStorageLocation, err)
129-
}
130-
bslSpec = &existingBSL.Spec
143+
// Build config map
144+
config := make(map[string]string)
145+
if o.Region != "" {
146+
config["region"] = o.Region
147+
}
148+
// Add any additional config provided via --config flag
149+
for k, v := range o.Config {
150+
config[k] = v
131151
}
132152

133-
bsl := &nacv1alpha1.NonAdminBackupStorageLocation{
153+
// Create the NABSL
154+
nabsl := &nacv1alpha1.NonAdminBackupStorageLocation{
134155
ObjectMeta: metav1.ObjectMeta{
135156
Name: o.Name,
136-
Namespace: o.NonAdminNamespace,
157+
Namespace: o.Namespace,
137158
},
138159
Spec: nacv1alpha1.NonAdminBackupStorageLocationSpec{
139-
BackupStorageLocationSpec: bslSpec,
160+
BackupStorageLocationSpec: &velerov1.BackupStorageLocationSpec{
161+
Provider: o.Provider,
162+
Config: config,
163+
Credential: &corev1.SecretKeySelector{
164+
LocalObjectReference: corev1.LocalObjectReference{
165+
Name: o.CredentialName,
166+
},
167+
Key: "cloud", // Standard key name for cloud credentials
168+
},
169+
StorageType: velerov1.StorageType{
170+
ObjectStorage: &velerov1.ObjectStorageLocation{
171+
Bucket: o.Bucket,
172+
Prefix: o.Prefix,
173+
},
174+
},
175+
},
140176
},
141177
}
142178

143-
if printed, err := output.PrintWithFormat(c, bsl); printed || err != nil {
179+
if printed, err := output.PrintWithFormat(c, nabsl); printed || err != nil {
144180
return err
145181
}
146182

147-
err := o.client.Create(context.Background(), bsl)
183+
err := o.client.Create(context.Background(), nabsl)
148184
if err != nil {
149185
return err
150186
}
151187

152-
fmt.Printf("NonAdminBackupStorageLocation %q created successfully.\n", bsl.Name)
188+
fmt.Printf("NonAdminBackupStorageLocation %q created successfully.\n", nabsl.Name)
189+
fmt.Printf("The controller will create a request for admin approval.\n")
190+
fmt.Printf("Use 'kubectl oadp nonadmin bsl request get' to view auto-created requests.\n")
153191
return nil
154192
}

cmd/non-admin/bsl/request.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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 bsl
18+
19+
import (
20+
"github.com/spf13/cobra"
21+
"github.com/vmware-tanzu/velero/pkg/client"
22+
)
23+
24+
// NewRequestCommand creates the "request" subcommand under bsl
25+
func NewRequestCommand(f client.Factory) *cobra.Command {
26+
c := &cobra.Command{
27+
Use: "request",
28+
Short: "Manage backup storage location approval requests",
29+
Long: "View and manage approval requests for backup storage locations. Requests are automatically created when users create backup storage locations and require admin approval.",
30+
}
31+
32+
c.AddCommand(
33+
NewRequestGetCommand(f),
34+
NewRequestDescribeCommand(f),
35+
NewRequestApproveCommand(f),
36+
NewRequestDenyCommand(f),
37+
)
38+
39+
return c
40+
}

0 commit comments

Comments
 (0)