Skip to content

Commit

Permalink
Feat/dynamic client (#6)
Browse files Browse the repository at this point in the history
* wip

* finalize

* fix

* cleanup

* fix
  • Loading branch information
Telemaco019 authored Jul 6, 2024
1 parent 6590234 commit c29730c
Show file tree
Hide file tree
Showing 16 changed files with 212 additions and 147 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,5 @@ go.work
go.work.sum
bin/
dist/

.DS_Store
60 changes: 60 additions & 0 deletions pkg/clients/duplik8s_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2024 Michele Zanotti <[email protected]>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package clients

import (
"context"
"github.com/telemaco019/duplik8s/pkg/core"
"github.com/telemaco019/duplik8s/pkg/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)

type Duplik8sClient struct {
client dynamic.Interface
}

func NewDuplik8sClient(opts utils.KubeOptions) (*Duplik8sClient, error) {
client, err := utils.NewDynamicClient(opts.Kubeconfig, opts.Kubecontext)
if err != nil {
return nil, err
}
return &Duplik8sClient{
client: client,
}, nil
}

func (c Duplik8sClient) ListDuplicable(
ctx context.Context,
resource schema.GroupVersionResource,
namespace string,
) ([]core.DuplicableObject, error) {
unstructuredList, err := c.client.Resource(resource).Namespace(namespace).List(ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
var objs []core.DuplicableObject
for _, u := range unstructuredList.Items {
objs = append(objs, core.NewDuplicable(u))
}
return objs, nil
}

func (c Duplik8sClient) ListDuplicated(ctx context.Context) ([]core.DuplicableObject, error) {
return nil, nil
}
19 changes: 12 additions & 7 deletions pkg/cmd/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,29 @@ package cmd

import (
"github.com/spf13/cobra"
"github.com/telemaco019/duplik8s/pkg/clients"
"github.com/telemaco019/duplik8s/pkg/core"
"github.com/telemaco019/duplik8s/pkg/duplicators"
"github.com/telemaco019/duplik8s/pkg/utils"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func NewDeployCmd(client core.Duplik8sClient) *cobra.Command {
factory := func(opts utils.KubeOptions) (core.Duplik8sClient, error) {
if client == nil {
return clients.NewDeploymentClient(opts)
func NewDeployCmd(duplicator core.Duplicator, client core.Client) *cobra.Command {
factory := func(opts utils.KubeOptions) (core.Duplicator, error) {
if duplicator == nil {
return duplicators.NewDeploymentClient(opts)
}
return client, nil
return duplicator, nil
}
deployCmd := &cobra.Command{
Use: "deploy",
Short: "Duplicate a Deployment.",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
run := newDuplicateCmd(factory, "Select a Deployment")
run := newDuplicateCmd(factory, client, schema.GroupVersionResource{
Group: "apps",
Version: "v1",
Resource: "deployments",
})
return run(cmd, args)
},
}
Expand Down
19 changes: 12 additions & 7 deletions pkg/cmd/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,29 @@ package cmd

import (
"github.com/spf13/cobra"
"github.com/telemaco019/duplik8s/pkg/clients"
"github.com/telemaco019/duplik8s/pkg/core"
"github.com/telemaco019/duplik8s/pkg/duplicators"
"github.com/telemaco019/duplik8s/pkg/utils"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func NewPodCmd(podClient core.Duplik8sClient) *cobra.Command {
factory := func(opts utils.KubeOptions) (core.Duplik8sClient, error) {
if podClient == nil {
return clients.NewPodClient(opts)
func NewPodCmd(duplicator core.Duplicator, client core.Client) *cobra.Command {
factory := func(opts utils.KubeOptions) (core.Duplicator, error) {
if duplicator == nil {
return duplicators.NewPodClient(opts)
}
return podClient, nil
return duplicator, nil
}
podCmd := &cobra.Command{
Use: "pod",
Short: "Duplicate a Pod.",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
run := newDuplicateCmd(factory, "Select a Pod")
run := newDuplicateCmd(factory, client, schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "pods",
})
return run(cmd, args)
},
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/cmd/pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func Test_NoPodsAvailable(t *testing.T) {
mocks.ListPodsResult{},
nil,
)
cmd := NewRootCmd(podClient, nil)
cmd := NewRootCmd(podClient, podClient)
output, err := test.ExecuteCommand(cmd, "pod")
assert.NotEmpty(t, output)
assert.Error(t, err)
Expand All @@ -40,7 +40,7 @@ func Test_Success(t *testing.T) {
mocks.ListPodsResult{},
nil,
)
cmd := NewRootCmd(podClient, nil)
cmd := NewRootCmd(podClient, podClient)
_, err := test.ExecuteCommand(cmd, "pod", "pod-1")
assert.NoError(t, err)
}
Expand All @@ -50,7 +50,7 @@ func Test_DuplicateError(t *testing.T) {
mocks.ListPodsResult{},
fmt.Errorf("error"),
)
cmd := NewRootCmd(podClient, nil)
cmd := NewRootCmd(podClient, podClient)
_, err := test.ExecuteCommand(cmd, "pod", "pod-1")
assert.EqualError(t, err, "error")
}
10 changes: 5 additions & 5 deletions pkg/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ import (
)

func NewRootCmd(
podClient core.Duplik8sClient,
deployClient core.Duplik8sClient,
duplicator core.Duplicator,
client core.Client,
) *cobra.Command {
rootCmd := &cobra.Command{
Use: "kubectl-duplicate",
Expand All @@ -51,9 +51,9 @@ func NewRootCmd(
configFlags.AddFlags(rootCmd.PersistentFlags())

// add subcommands
rootCmd.AddCommand(NewPodCmd(podClient))
rootCmd.AddCommand(NewDeployCmd(deployClient))
rootCmd.AddCommand(NewStatefulSetCmd(deployClient))
rootCmd.AddCommand(NewPodCmd(duplicator, client))
rootCmd.AddCommand(NewDeployCmd(duplicator, client))
rootCmd.AddCommand(NewStatefulSetCmd(duplicator, client))

return rootCmd
}
52 changes: 42 additions & 10 deletions pkg/cmd/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,36 @@
package cmd

import (
"context"
"fmt"
"github.com/spf13/cobra"
"github.com/telemaco019/duplik8s/pkg/clients"
"github.com/telemaco019/duplik8s/pkg/cmd/flags"
"github.com/telemaco019/duplik8s/pkg/core"
"github.com/telemaco019/duplik8s/pkg/utils"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"k8s.io/apimachinery/pkg/runtime/schema"
)

type duplik8sClientFactory func(opts utils.KubeOptions) (core.Duplik8sClient, error)
type duplicatorFactory func(opts utils.KubeOptions) (core.Duplicator, error)

func newDuplicateCmd(factory duplik8sClientFactory, selectMessage string) func(cmd *cobra.Command, args []string) error {
func newDuplicateCmd(newDuplicator duplicatorFactory, client core.Client, gvr schema.GroupVersionResource) func(cmd *cobra.Command, args []string) error {
return func(cmd *cobra.Command, args []string) error {
opts, err := NewKubeOptions(cmd, args)
if err != nil {
return err
}
client, err := factory(opts)
duplicator, err := newDuplicator(opts)
if err != nil {
return err
}
if client == nil {
client, err = clients.NewDuplik8sClient(opts)
if err != nil {
return err
}
}
cmdOverride, err := cmd.Flags().GetStringSlice(flags.COMMAND_OVERRIDE)
if err != nil {
return err
Expand All @@ -51,17 +63,37 @@ func newDuplicateCmd(factory duplik8sClientFactory, selectMessage string) func(c
Args: argsOverride,
}

// If available, duplicate the resource provided as argument
var obj core.DuplicableObject
if len(args) == 0 {
obj, err = utils.SelectItem(client, opts.Namespace, selectMessage)
if err != nil {
return err
if len(args) > 0 {
obj = core.DuplicableObject{
Name: args[0],
Namespace: opts.Namespace,
}
} else {
obj = core.NewPod(args[0], opts.Namespace)
return duplicator.Duplicate(obj, options)
}

return client.Duplicate(obj, options)
// Otherwise, list available resources
objs, err := client.ListDuplicable(
context.Background(),
gvr,
opts.Namespace,
)
if err != nil {
return err
}
if len(objs) == 0 {
return fmt.Errorf("no %s available in namespace %q", gvr.Resource, opts.Namespace)
}
caser := cases.Title(language.English)
obj, err = utils.SelectItem(
objs,
fmt.Sprintf("%s [%s]", caser.String(gvr.Resource), opts.Namespace),
)
if err != nil {
return err
}
return duplicator.Duplicate(obj, options)
}
}

Expand Down
19 changes: 12 additions & 7 deletions pkg/cmd/statefulset.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,29 @@ package cmd

import (
"github.com/spf13/cobra"
"github.com/telemaco019/duplik8s/pkg/clients"
"github.com/telemaco019/duplik8s/pkg/core"
"github.com/telemaco019/duplik8s/pkg/duplicators"
"github.com/telemaco019/duplik8s/pkg/utils"
"k8s.io/apimachinery/pkg/runtime/schema"
)

func NewStatefulSetCmd(client core.Duplik8sClient) *cobra.Command {
factory := func(opts utils.KubeOptions) (core.Duplik8sClient, error) {
if client == nil {
return clients.NewStatefulSetClient(opts)
func NewStatefulSetCmd(duplicator core.Duplicator, client core.Client) *cobra.Command {
factory := func(opts utils.KubeOptions) (core.Duplicator, error) {
if duplicator == nil {
return duplicators.NewStatefulSetClient(opts)
}
return client, nil
return duplicator, nil
}
deployCmd := &cobra.Command{
Use: "statefulset",
Short: "Duplicate a StatefulSet.",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
run := newDuplicateCmd(factory, "Select a StatefulSet")
run := newDuplicateCmd(factory, client, schema.GroupVersionResource{
Group: "apps",
Version: "v1",
Resource: "statefulsets",
})
return run(cmd, args)
},
}
Expand Down
22 changes: 0 additions & 22 deletions pkg/core/client.go

This file was deleted.

Loading

0 comments on commit c29730c

Please sign in to comment.