mirror of
https://github.com/vee1e/runtimeclass-debugger.git
synced 2026-09-01 18:27:58 +00:00
golangci-lint v2 with its default linters flags ten unchecked Close returns (errcheck) that v1.64 did not report: the database and rows handles in the SQLite store, the MetaServer response body and probe connection, and the test fixtures.
139 lines
3.8 KiB
Go
139 lines
3.8 KiB
Go
package bridge
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"sort"
|
|
|
|
nodev1 "k8s.io/api/node/v1"
|
|
|
|
_ "github.com/mattn/go-sqlite3"
|
|
)
|
|
|
|
// ErrStoreNotFound is returned when the local SQLite store file does not
|
|
// exist.
|
|
var ErrStoreNotFound = errors.New("local store not found")
|
|
|
|
// Store reads Kubernetes API objects from the KubeEdge local SQLite store
|
|
// (/var/lib/kubeedge/edgecore.db by default).
|
|
type Store struct {
|
|
path string
|
|
db *sql.DB
|
|
}
|
|
|
|
// OpenStore opens the KubeEdge local SQLite store read-only.
|
|
func OpenStore(path string) (*Store, error) {
|
|
if _, err := os.Stat(path); err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("%w: %s", ErrStoreNotFound, path)
|
|
}
|
|
return nil, err
|
|
}
|
|
db, err := sql.Open("sqlite3", "file:"+path+"?mode=ro")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := db.Ping(); err != nil {
|
|
_ = db.Close()
|
|
return nil, err
|
|
}
|
|
return &Store{path: path, db: db}, nil
|
|
}
|
|
|
|
// Close closes the underlying database handle.
|
|
func (s *Store) Close() error {
|
|
return s.db.Close()
|
|
}
|
|
|
|
// ListRuntimeClasses returns the RuntimeClass objects stored locally.
|
|
// Both the meta_v2 table (key format /<group>/<version>/<resource>/<ns>/<name>)
|
|
// and the legacy meta table (key format <ns>/<type>/<name>) are scanned, so
|
|
// the tool keeps working regardless of which store generation the node runs.
|
|
// Column naming varies between KubeEdge versions (group_version_resource vs
|
|
// groupversionresource), so the schema is introspected first.
|
|
func (s *Store) ListRuntimeClasses() ([]nodev1.RuntimeClass, error) {
|
|
classes := []nodev1.RuntimeClass{}
|
|
seen := map[string]bool{}
|
|
classes, seen = s.scanTable("meta_v2", classes, seen)
|
|
classes, _ = s.scanTable("meta", classes, seen)
|
|
sort.Slice(classes, func(i, j int) bool { return classes[i].Name < classes[j].Name })
|
|
return classes, nil
|
|
}
|
|
|
|
func (s *Store) scanTable(table string, classes []nodev1.RuntimeClass, seen map[string]bool) ([]nodev1.RuntimeClass, map[string]bool) {
|
|
columns, err := s.tableColumns(table)
|
|
if err != nil || !hasColumn(columns, "value") {
|
|
return classes, seen
|
|
}
|
|
where := "key LIKE '%/runtimeclass%'"
|
|
if hasColumn(columns, "group_version_resource") {
|
|
where += " OR group_version_resource LIKE '%runtimeclass%'"
|
|
} else if hasColumn(columns, "groupversionresource") {
|
|
where += " OR groupversionresource LIKE '%runtimeclass%'"
|
|
} else if hasColumn(columns, "type") {
|
|
where += " OR type LIKE '%runtimeclass%'"
|
|
}
|
|
rows, err := s.db.Query(fmt.Sprintf("SELECT value FROM %s WHERE %s", table, where))
|
|
if err != nil {
|
|
return classes, seen
|
|
}
|
|
return appendClasses(rows, classes, seen)
|
|
}
|
|
|
|
func (s *Store) tableColumns(table string) ([]string, error) {
|
|
rows, err := s.db.Query(fmt.Sprintf("SELECT name FROM pragma_table_info('%s')", table))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { _ = rows.Close() }()
|
|
var columns []string
|
|
for rows.Next() {
|
|
var name string
|
|
if err := rows.Scan(&name); err != nil {
|
|
return nil, err
|
|
}
|
|
columns = append(columns, name)
|
|
}
|
|
return columns, rows.Err()
|
|
}
|
|
|
|
func hasColumn(columns []string, name string) bool {
|
|
for _, c := range columns {
|
|
if c == name {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func appendClasses(rows *sql.Rows, classes []nodev1.RuntimeClass, seen map[string]bool) ([]nodev1.RuntimeClass, map[string]bool) {
|
|
defer func() { _ = rows.Close() }()
|
|
for rows.Next() {
|
|
var raw string
|
|
if err := rows.Scan(&raw); err != nil {
|
|
continue
|
|
}
|
|
var probe struct {
|
|
Kind string `json:"kind"`
|
|
}
|
|
if err := json.Unmarshal([]byte(raw), &probe); err != nil {
|
|
continue
|
|
}
|
|
if probe.Kind != "RuntimeClass" {
|
|
continue
|
|
}
|
|
var rc nodev1.RuntimeClass
|
|
if err := json.Unmarshal([]byte(raw), &rc); err != nil {
|
|
continue
|
|
}
|
|
if rc.Name == "" || seen[rc.Name] {
|
|
continue
|
|
}
|
|
seen[rc.Name] = true
|
|
classes = append(classes, rc)
|
|
}
|
|
return classes, seen
|
|
}
|