diff --git a/internal/db/duckdb/db.go b/internal/db/duckdb/db.go index 2b8c9e0..52f0709 100644 --- a/internal/db/duckdb/db.go +++ b/internal/db/duckdb/db.go @@ -5,16 +5,12 @@ import ( "database/sql" "database/sql/driver" "fmt" - "path/filepath" "github.com/duckdb/duckdb-go/v2" log "github.com/sirupsen/logrus" ) -func initConnector(dataDir string) (*duckdb.Connector, error) { - dbDir := filepath.Join(dataDir, duckdbDir) - dbFile := filepath.Join(dbDir, "duckdb.db") - +func initConnector(dbFile string) (*duckdb.Connector, error) { return duckdb.NewConnector(dbFile, nil) } diff --git a/internal/db/duckdb/init.go b/internal/db/duckdb/init.go index 48c4c22..8656a49 100644 --- a/internal/db/duckdb/init.go +++ b/internal/db/duckdb/init.go @@ -12,7 +12,7 @@ import ( log "github.com/sirupsen/logrus" ) -const duckdbDir = "duckdb" +const dbFileName = "duckdb.db" var ( //go:embed sql/init.sql @@ -20,28 +20,40 @@ var ( ) func InitDB(ctx context.Context, dataDir string, k8sInfo *k8s.Info) error { - dbDir := filepath.Join(dataDir, duckdbDir) - err := resetDBDirectory(dbDir) + err := resetDBDirectory(dataDir) if err != nil { return err } - connector, err := initConnector(dataDir) + dbFile := filepath.Join(dataDir, dbFileName) + connector, err := initConnector(dbFile) if err != nil { return fmt.Errorf("failed to initialize DB connector: %w", err) } + defer func() { + if err := connector.Close(); err != nil { + log.Errorf("failed to close raw DB connection: %s", err) + } + }() + db, con, err := initConnection(ctx, connector) if err != nil { return err } - defer func(db *sql.DB) { + defer func() { + if err := con.Close(); err != nil { + log.Errorf("failed to close raw DB connection: %s", err) + } + }() + + defer func() { err := db.Close() if err != nil { log.Errorf("failed to close DB connection: %s", err) } - }(db) + }() err = db.Ping() if err != nil { diff --git a/internal/db/duckdb/init_test.go b/internal/db/duckdb/init_test.go new file mode 100644 index 0000000..b0197a7 --- /dev/null +++ b/internal/db/duckdb/init_test.go @@ -0,0 +1,55 @@ +package duckdb_test + +import ( + "context" + "testing" + "time" + + "github.com/Hexta/k8s-tools/internal/db" + "github.com/Hexta/k8s-tools/internal/k8s" + "github.com/Hexta/k8s-tools/internal/k8s/fetch" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + fakeapiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + dynamicfake "k8s.io/client-go/dynamic/fake" + k8sfake "k8s.io/client-go/kubernetes/fake" + "k8s.io/client-go/kubernetes/scheme" +) + +func TestDBInit(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + resources := []runtime.Object{ + &v1.Node{ + ObjectMeta: metav1.ObjectMeta{Name: "node1"}, + }, + } + + // Initialize fake clients + clientSet := k8sfake.NewSimpleClientset(resources...) + dynamicClient := dynamicfake.NewSimpleDynamicClient(scheme.Scheme) + apiExtClient := fakeapiextensions.NewSimpleClientset() + + k8sInfo := k8s.NewInfo(ctx, clientSet, dynamicClient, apiExtClient) + + // Create a temporary cache directory for the database + dbDir := t.TempDir() + + // Fetch info (using fake clients) + err := k8sInfo.Fetch(fetch.Options{RetryInitialInterval: 100 * time.Millisecond, RetryMaxAttempts: 0}) + require.NoErrorf(t, err, "Failed to fetch K8s info") + + // Initialize database + err = db.InitDB(ctx, dbDir, k8sInfo) + require.NoErrorf(t, err, "Failed to init DB") + + // Query the database + data, err := db.Query(ctx, dbDir, "SELECT * FROM k8s.nodes") + require.NoErrorf(t, err, "Failed to query DB") + require.NotNil(t, data) +} diff --git a/internal/db/duckdb/query.go b/internal/db/duckdb/query.go index 4876efb..6b9497b 100644 --- a/internal/db/duckdb/query.go +++ b/internal/db/duckdb/query.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "path/filepath" "slices" "github.com/Hexta/k8s-tools/internal/format" @@ -11,7 +12,8 @@ import ( ) func Query(ctx context.Context, dataDir string, q string) (*format.Data, error) { - connector, err := initConnector(dataDir) + dbFile := filepath.Join(dataDir, dbFileName) + connector, err := initConnector(dbFile) if err != nil { return nil, err diff --git a/internal/k8s/customresource/fetch.go b/internal/k8s/customresource/fetch.go index 811f332..3f7bcf5 100644 --- a/internal/k8s/customresource/fetch.go +++ b/internal/k8s/customresource/fetch.go @@ -14,7 +14,7 @@ import ( "k8s.io/client-go/dynamic" ) -func Fetch(ctx context.Context, dynamicClient *dynamic.DynamicClient, apiExtClient apiextensionsclient.Interface) (InfoList, error) { +func Fetch(ctx context.Context, dynamicClient dynamic.Interface, apiExtClient apiextensionsclient.Interface) (InfoList, error) { crdList, err := apiExtClient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{}) if err != nil { return nil, fmt.Errorf("failed to list custom resource definitions: %w", err) diff --git a/internal/k8s/deployment/fetch.go b/internal/k8s/deployment/fetch.go index cdf3de5..1d787e0 100644 --- a/internal/k8s/deployment/fetch.go +++ b/internal/k8s/deployment/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { deployments := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/ds/fetch.go b/internal/k8s/ds/fetch.go index d298821..e0779d6 100644 --- a/internal/k8s/ds/fetch.go +++ b/internal/k8s/ds/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/endpointslices/fetch.go b/internal/k8s/endpointslices/fetch.go index ffd7a5e..d1a61cd 100644 --- a/internal/k8s/endpointslices/fetch.go +++ b/internal/k8s/endpointslices/fetch.go @@ -11,7 +11,7 @@ import ( ) // Fetch retrieves a list of endpoint information from all namespaces using the provided Kubernetes clientset. -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/event/fetch.go b/internal/k8s/event/fetch.go index 08dcb77..42fa618 100644 --- a/internal/k8s/event/fetch.go +++ b/internal/k8s/event/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/hpa/fetch.go b/internal/k8s/hpa/fetch.go index eb8a6dd..2952138 100644 --- a/internal/k8s/hpa/fetch.go +++ b/internal/k8s/hpa/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/info.go b/internal/k8s/info.go index 16d16b0..0f6f7bb 100644 --- a/internal/k8s/info.go +++ b/internal/k8s/info.go @@ -46,8 +46,8 @@ type Info struct { Tolerations TolerationList PVCVolumes PVCVolumeList ctx context.Context - clientset *kubernetes.Clientset - dynamicClient *dynamic.DynamicClient + clientset kubernetes.Interface + dynamicClient dynamic.Interface apiExtClient apiextensionsclient.Interface } @@ -81,7 +81,7 @@ type PVCVolume struct { type PVCVolumeList []*PVCVolume -func NewInfo(ctx context.Context, clientset *kubernetes.Clientset, dynamicClient *dynamic.DynamicClient, apiExtClient apiextensionsclient.Interface) *Info { +func NewInfo(ctx context.Context, clientset kubernetes.Interface, dynamicClient dynamic.Interface, apiExtClient apiextensionsclient.Interface) *Info { return &Info{ ctx: ctx, clientset: clientset, diff --git a/internal/k8s/node/fetch.go b/internal/k8s/node/fetch.go index 7c42026..b5ac384 100644 --- a/internal/k8s/node/fetch.go +++ b/internal/k8s/node/fetch.go @@ -15,7 +15,7 @@ import ( const instanceTypeLabel = "node.kubernetes.io/instance-type" -func Fetch(ctx context.Context, clientset *kubernetes.Clientset, opts fetch.Options) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface, opts fetch.Options) (InfoList, error) { nodes := make(InfoList, 0, 10000) podsPerNode, err := listPodsPerNode(ctx, clientset) @@ -111,7 +111,7 @@ func getAddressMap(addresses []apicorev1.NodeAddress) map[string]string { return addrMap } -func listPodsPerNode(ctx context.Context, clientset *kubernetes.Clientset) (map[string][]*apicorev1.Pod, error) { +func listPodsPerNode(ctx context.Context, clientset kubernetes.Interface) (map[string][]*apicorev1.Pod, error) { podsPerNode := make(map[string][]*apicorev1.Pod) list, err := clientset.CoreV1().Pods("").List(ctx, v1.ListOptions{}) diff --git a/internal/k8s/pod/fetch.go b/internal/k8s/pod/fetch.go index 34633bf..8c29f85 100644 --- a/internal/k8s/pod/fetch.go +++ b/internal/k8s/pod/fetch.go @@ -12,7 +12,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { pods := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/pv/fetch.go b/internal/k8s/pv/fetch.go index e6f8e7d..e44feb9 100644 --- a/internal/k8s/pv/fetch.go +++ b/internal/k8s/pv/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/pvc/fetch.go b/internal/k8s/pvc/fetch.go index 766a231..1f46560 100644 --- a/internal/k8s/pvc/fetch.go +++ b/internal/k8s/pvc/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/service/fetch.go b/internal/k8s/service/fetch.go index 508cf37..484551f 100644 --- a/internal/k8s/service/fetch.go +++ b/internal/k8s/service/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) { diff --git a/internal/k8s/sts/fetch.go b/internal/k8s/sts/fetch.go index c127e4d..1667e0d 100644 --- a/internal/k8s/sts/fetch.go +++ b/internal/k8s/sts/fetch.go @@ -9,7 +9,7 @@ import ( "k8s.io/client-go/kubernetes" ) -func Fetch(ctx context.Context, clientset *kubernetes.Clientset) (InfoList, error) { +func Fetch(ctx context.Context, clientset kubernetes.Interface) (InfoList, error) { infoList := make(InfoList, 0, 10000) err := k8sutil.Paginate(ctx, func(opts v1.ListOptions) (string, error) {