@@ -5,25 +5,39 @@ import (
55 "errors"
66 "fmt"
77
8+ "encoding/json"
9+
810 mocov1beta2 "github.com/cybozu-go/moco/api/v1beta2"
911 "github.com/cybozu-go/moco/pkg/constants"
1012 "github.com/spf13/cobra"
1113 apierrors "k8s.io/apimachinery/pkg/api/errors"
1214 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1315 "k8s.io/apimachinery/pkg/types"
14- "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil "
16+ "sigs.k8s.io/controller-runtime/pkg/client "
1517)
1618
1719var credentialConfig struct {
1820 user string
1921 format string
2022}
2123
22- // credentialCmd represents the credential parent command
24+ // credentialCmd represents the credential parent command.
25+ // When called without a subcommand (e.g. "credential CLUSTER_NAME"),
26+ // it falls back to "show" for backward compatibility.
2327var credentialCmd = & cobra.Command {
24- Use : "credential" ,
28+ Use : "credential [CLUSTER_NAME] " ,
2529 Short : "Manage MySQL credentials" ,
26- Long : "Manage MySQL credentials for a MOCO cluster." ,
30+ Long : "Manage MySQL credentials for a MOCO cluster. When called with a cluster name and no subcommand, shows the credential (backward compatible)." ,
31+ Args : cobra .MaximumNArgs (1 ),
32+ RunE : func (cmd * cobra.Command , args []string ) error {
33+ if len (args ) == 0 {
34+ return cmd .Help ()
35+ }
36+ return credentialShow (cmd .Context (), args [0 ])
37+ },
38+ ValidArgsFunction : func (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
39+ return mysqlClusterCandidates (cmd .Context (), cmd , args , toComplete )
40+ },
2741}
2842
2943// credentialShowCmd shows the credential of a specified user
@@ -119,9 +133,7 @@ func credentialRotate(ctx context.Context, clusterName string) error {
119133 DiscardOldPassword : false ,
120134 },
121135 }
122- if err := controllerutil .SetOwnerReference (cluster , cr , kubeClient .Scheme ()); err != nil {
123- return fmt .Errorf ("failed to set ownerReference: %w" , err )
124- }
136+ // ownerReference is set by the controller on first reconcile.
125137 if err := kubeClient .Create (ctx , cr ); err != nil {
126138 return fmt .Errorf ("failed to create CredentialRotation: %w" , err )
127139 }
@@ -138,12 +150,20 @@ func credentialRotate(ctx context.Context, clusterName string) error {
138150 return fmt .Errorf ("cannot rotate: rotation is in progress (phase=%s)" , phase )
139151 }
140152
141- cr .Spec .RotationGeneration ++
142- cr .Spec .DiscardOldPassword = false
143- if err := kubeClient .Update (ctx , cr ); err != nil {
144- return fmt .Errorf ("failed to update CredentialRotation: %w" , err )
153+ newGen := cr .Spec .RotationGeneration + 1
154+ patch , err := json .Marshal (map [string ]interface {}{
155+ "spec" : map [string ]interface {}{
156+ "rotationGeneration" : newGen ,
157+ "discardOldPassword" : false ,
158+ },
159+ })
160+ if err != nil {
161+ return fmt .Errorf ("failed to marshal patch: %w" , err )
162+ }
163+ if err := kubeClient .Patch (ctx , cr , client .RawPatch (types .MergePatchType , patch )); err != nil {
164+ return fmt .Errorf ("failed to patch CredentialRotation: %w" , err )
145165 }
146- fmt .Printf ("Updated CredentialRotation %s/%s with rotationGeneration=%d\n " , namespace , clusterName , cr . Spec . RotationGeneration )
166+ fmt .Printf ("Updated CredentialRotation %s/%s with rotationGeneration=%d\n " , namespace , clusterName , newGen )
147167 return nil
148168}
149169
@@ -160,27 +180,38 @@ func credentialDiscard(ctx context.Context, clusterName string) error {
160180 return fmt .Errorf ("cannot discard: phase must be Rotated, currently %q" , cr .Status .Phase )
161181 }
162182
163- cr .Spec .DiscardOldPassword = true
164- if err := kubeClient .Update (ctx , cr ); err != nil {
165- return fmt .Errorf ("failed to update CredentialRotation: %w" , err )
183+ patch , err := json .Marshal (map [string ]interface {}{
184+ "spec" : map [string ]interface {}{
185+ "discardOldPassword" : true ,
186+ },
187+ })
188+ if err != nil {
189+ return fmt .Errorf ("failed to marshal patch: %w" , err )
190+ }
191+ if err := kubeClient .Patch (ctx , cr , client .RawPatch (types .MergePatchType , patch )); err != nil {
192+ return fmt .Errorf ("failed to patch CredentialRotation: %w" , err )
166193 }
167194 fmt .Printf ("Set discardOldPassword=true on CredentialRotation %s/%s\n " , namespace , clusterName )
168195 return nil
169196}
170197
171198func init () {
172- // Show subcommand flags
173- fs := credentialShowCmd .Flags ()
199+ // Flags on the parent command for backward compatibility
200+ // ("kubectl moco credential -u moco-admin CLUSTER_NAME").
201+ fs := credentialCmd .Flags ()
174202 fs .StringVarP (& credentialConfig .user , "mysql-user" , "u" , constants .ReadOnlyUser , "User for login to mysql" )
175203 fs .StringVar (& credentialConfig .format , "format" , "plain" , "The format of output [`plain` or `mycnf`]" )
176204
177- _ = credentialShowCmd .RegisterFlagCompletionFunc ("mysql-user" , func (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
205+ _ = credentialCmd .RegisterFlagCompletionFunc ("mysql-user" , func (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
178206 return []string {constants .ReadOnlyUser , constants .WritableUser , constants .AdminUser }, cobra .ShellCompDirectiveDefault
179207 })
180- _ = credentialShowCmd .RegisterFlagCompletionFunc ("format" , func (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
208+ _ = credentialCmd .RegisterFlagCompletionFunc ("format" , func (cmd * cobra.Command , args []string , toComplete string ) ([]string , cobra.ShellCompDirective ) {
181209 return []string {"plain" , "mycnf" }, cobra .ShellCompDirectiveDefault
182210 })
183211
212+ // "show" subcommand shares the same flags via the parent.
213+ credentialShowCmd .Flags ().AddFlagSet (credentialCmd .Flags ())
214+
184215 credentialCmd .AddCommand (credentialShowCmd )
185216 credentialCmd .AddCommand (credentialRotateCmd )
186217 credentialCmd .AddCommand (credentialDiscardCmd )
0 commit comments