From caa516d4b9a1056903c393631ce3b6acf819d16b Mon Sep 17 00:00:00 2001 From: Dan Fuhry Date: Wed, 22 Jul 2026 23:41:42 -0400 Subject: [PATCH] [k8s] sqpkg CSI driver No, this was not vibe coded. Actually there is nothing at all in this change that came from an LLM. I don't want to build container images. I want to ship a generic, frequently-updated base archlinux container, and push binaries, configs, etc. separately. The plan is to build squashfs packages composed of a series of bazel targets and upload those to MinIO, then pull them down when a container wants to mount them. Getting mksquashfs to work in Bazel without it complaining about including system headers because it can't decide if it's hermetic or not is proving to be the greatest challenge in this endeavor. --- MODULE.bazel | 16 ++ cmd/sqpkg_csi_driver/BUILD.bazel | 20 ++ cmd/sqpkg_csi_driver/main.go | 87 ++++++ go.mod | 25 +- go.sum | 28 ++ k8s/sqpkg/csi_driver/BUILD.bazel | 26 ++ k8s/sqpkg/csi_driver/controller.go | 100 +++++++ k8s/sqpkg/csi_driver/identity.go | 69 +++++ k8s/sqpkg/csi_driver/node.go | 248 ++++++++++++++++++ k8s/sqpkg/csi_driver/s3.go | 141 ++++++++++ ..._0001-add-unimplemented-server-types.patch | 39 +++ 11 files changed, 788 insertions(+), 11 deletions(-) create mode 100644 cmd/sqpkg_csi_driver/BUILD.bazel create mode 100644 cmd/sqpkg_csi_driver/main.go create mode 100644 k8s/sqpkg/csi_driver/BUILD.bazel create mode 100644 k8s/sqpkg/csi_driver/controller.go create mode 100644 k8s/sqpkg/csi_driver/identity.go create mode 100644 k8s/sqpkg/csi_driver/node.go create mode 100644 k8s/sqpkg/csi_driver/s3.go create mode 100644 thirdparty/kubernetes/csi_common_0001-add-unimplemented-server-types.patch diff --git a/MODULE.bazel b/MODULE.bazel index b0c655d..ae9499b 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -123,8 +123,22 @@ go_deps.module_override( ], path = "github.com/caarlos0/go-version", ) +go_deps.gazelle_override( + directives = [ + "gazelle:proto disable_global", + ], + path = "github.com/container-storage-interface/spec", +) +go_deps.module_override( + patch_strip = 1, + patches = [ + "thirdparty/kubernetes/csi_common_0001-add-unimplemented-server-types.patch", + ], + path = "github.com/kubernetes-csi/drivers", +) use_repo( go_deps, + "com_github_container_storage_interface_spec", "com_github_coredns_caddy", "com_github_coredns_coredns", "com_github_coredns_records", @@ -145,6 +159,7 @@ use_repo( "com_github_hashicorp_golang_lru", "com_github_hashicorp_golang_lru_v2", "com_github_keybase_go_keychain", + "com_github_kubernetes_csi_drivers", "com_github_mdlayher_apcupsd", "com_github_miekg_dns", "com_github_minio_minio_go_v7", @@ -171,6 +186,7 @@ use_repo( "io_etcd_go_etcd_client_pkg_v3", "io_etcd_go_etcd_client_v3", "io_etcd_go_etcd_server_v3", + "io_k8s_klog_v2", "net_howett_plist", "org_golang_google_grpc", "org_golang_google_protobuf", diff --git a/cmd/sqpkg_csi_driver/BUILD.bazel b/cmd/sqpkg_csi_driver/BUILD.bazel new file mode 100644 index 0000000..442edb9 --- /dev/null +++ b/cmd/sqpkg_csi_driver/BUILD.bazel @@ -0,0 +1,20 @@ +load("//bazel:go.bzl", "go_multi_binary", "go_multi_library") + +go_multi_library( + name = "sqpkg_csi_driver_lib", + srcs = ["main.go"], + importpath = "go.fuhry.dev/runtime/cmd/sqpkg_csi_driver", + visibility = ["//visibility:private"], + deps = [ + "//constants", + "//k8s/sqpkg/csi_driver", + "@com_github_kubernetes_csi_drivers//pkg/csi-common", + "@io_k8s_klog_v2//:klog", + ], +) + +go_multi_binary( + name = "sqpkg_csi_driver", + embed = [":sqpkg_csi_driver_lib"], + visibility = ["//visibility:public"], +) diff --git a/cmd/sqpkg_csi_driver/main.go b/cmd/sqpkg_csi_driver/main.go new file mode 100644 index 0000000..2e185b1 --- /dev/null +++ b/cmd/sqpkg_csi_driver/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "flag" + "fmt" + "net/url" + "os" + "path" + "strings" + + csicommon "github.com/kubernetes-csi/drivers/pkg/csi-common" + "k8s.io/klog/v2" + + "go.fuhry.dev/runtime/constants" + "go.fuhry.dev/runtime/k8s/sqpkg/csi_driver" +) + +func main() { + var ( + endpoint = flag.String("endpoint", "unix:///run/"+constants.OrgSlug+"/sqpkg-csi.sock", "CSI endpoint") + nodeID = flag.String("node-id", "", "Node ID") + versionOnly = flag.Bool("version", false, "show version and exit") + cacheDir = flag.String("cache-dir", "", "directory where cached packages will be stored") + driverName = flag.String("driver-name", "", "CSI driver name") + + s3Endpoint = flag.String("sqpkg.s3-endpoint", "", "S3 endpoint URL") + s3Bucket = flag.String("sqpkg.s3-bucket", "", "S3 bucket name") + s3AccessKey = flag.String("sqpkg.s3-access-key", "", "S3 access key") + s3SecretKey = flag.String("sqpkg.s3-secret-key", "", "S3 secret key") + ) + flag.Parse() + + if *versionOnly { + fmt.Println(constants.Version) + return + } + + if *nodeID == "" { + *nodeID = os.Getenv("NODE_ID") + if *nodeID == "" { + fmt.Println("node-id is required") + os.Exit(1) + } + } + + endpointUrl, err := url.Parse(*endpoint) + if err != nil { + klog.Fatalf("invalid endpoint url %q: %v", *endpoint, err) + } + + if endpointUrl.Scheme == "unix" { + endpointSocketPath := "/" + strings.TrimLeft(endpointUrl.Host+"/"+strings.TrimLeft(endpointUrl.Path, "/"), "/") + dir := path.Dir(endpointSocketPath) + err = os.MkdirAll(dir, os.FileMode(0o755)) + if err != nil && !os.IsExist(err) { + klog.Fatalf("failed to create directory for socket %q: %v", endpointSocketPath, err) + } + klog.Infof("created directory %q for socket %q", dir, endpointSocketPath) + } + + var idOpts []csi_driver.IdentityServerOption + if *driverName != "" { + idOpts = append(idOpts, csi_driver.WithDriverName(*driverName)) + } + + var nodeOpts []csi_driver.NodeServerOption + if *cacheDir != "" { + nodeOpts = append(nodeOpts, csi_driver.WithCacheDir(*cacheDir)) + } + + if *s3Endpoint != "" && *s3Bucket != "" { + nodeOpts = append(nodeOpts, csi_driver.WithS3Params(*s3Endpoint, *s3Bucket)) + } + if *s3AccessKey != "" && *s3SecretKey != "" { + nodeOpts = append(nodeOpts, csi_driver.WithS3Credentials(*s3AccessKey, *s3SecretKey)) + } + + // Create the driver + identityServer := csi_driver.NewIdentityServer(idOpts...) + controllerServer := csi_driver.NewControllerServer() + nodeServer := csi_driver.NewNodeServer(*nodeID, nodeOpts...) + + // Start the gRPC server + server := csicommon.NewNonBlockingGRPCServer() + server.Start(*endpoint, identityServer, controllerServer, nodeServer) + server.Wait() +} diff --git a/go.mod b/go.mod index 203a4ff..cf8f773 100644 --- a/go.mod +++ b/go.mod @@ -148,7 +148,7 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/ebitengine/purego v0.10.0 // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/expr-lang/expr v1.17.8 // indirect github.com/farsightsec/golang-framestream v0.3.0 // indirect @@ -168,7 +168,7 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/gobwas/glob v0.2.3 // indirect @@ -218,9 +218,10 @@ require ( github.com/klauspost/pgzip v1.2.6 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/kubernetes-csi/csi-lib-utils v0.24.0 // indirect github.com/linkdata/deadlock v0.5.5 // indirect github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -359,22 +360,22 @@ require ( gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/api v0.35.3 // indirect - k8s.io/apimachinery v0.35.3 // indirect - k8s.io/client-go v0.35.3 // indirect - k8s.io/klog/v2 v2.140.0 // indirect - k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect - k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + k8s.io/api v0.36.0 // indirect + k8s.io/apimachinery v0.36.0 // indirect + k8s.io/client-go v0.36.0 // indirect + k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect + k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/mcs-api v0.4.1 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) require ( github.com/DataDog/sketches-go v1.4.8 github.com/bazelbuild/rules_go v0.61.1 + github.com/container-storage-interface/spec v1.12.0 github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495 github.com/coredns/coredns v1.14.3 github.com/coredns/records v0.0.0-20230310133434-a3157e710d9e @@ -386,6 +387,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/goreleaser/nfpm/v2 v2.44.1 github.com/hashicorp/golang-lru/v2 v2.0.7 + github.com/kubernetes-csi/drivers v1.0.2 go.etcd.io/etcd/api/v3 v3.6.10 go.etcd.io/etcd/client/pkg/v3 v3.6.10 go.etcd.io/etcd/server/v3 v3.6.6 @@ -395,7 +397,8 @@ require ( golang.org/x/net v0.55.0 // indirect golang.org/x/text v0.37.0 google.golang.org/grpc v1.81.1 - google.golang.org/protobuf v1.36.11 + google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af + k8s.io/klog/v2 v2.140.0 ) replace github.com/keybase/go-keychain => github.com/fuhry/go-keychain v0.0.0-20231123062916-83673b79739d diff --git a/go.sum b/go.sum index e15cf9c..f274d9f 100644 --- a/go.sum +++ b/go.sum @@ -220,6 +220,8 @@ github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/container-storage-interface/spec v1.12.0 h1:zrFOEqpR5AghNaaDG4qyedwPBqU2fU0dWjLQMP/azK0= +github.com/container-storage-interface/spec v1.12.0/go.mod h1:txsm+MA2B2WDa5kW69jNbqPnvTtfvZma7T/zsAZ9qX8= github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495 h1:JFeOmbjLnVRhvmLHyuO3M1pfXWlPWpwkdM8UqXZRtBg= github.com/coredns/caddy v1.1.4-0.20250930002214-15135a999495/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/coredns v1.14.3 h1:hWWoTdONblKIWhC8QPkxLEGIbewhR5xyTedqLVPsvvE= @@ -290,6 +292,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/expr-lang/expr v1.17.8 h1:W1loDTT+0PQf5YteHSTpju2qfUfNoBt4yw9+wOEU9VM= @@ -356,6 +360,8 @@ github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1 github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= @@ -548,6 +554,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kubernetes-csi/csi-lib-utils v0.24.0 h1:hpL5ecxtr07/DNIF9Qn/gbNG/ZlgMSMZxfRtgfVm9tY= +github.com/kubernetes-csi/csi-lib-utils v0.24.0/go.mod h1:JbvkvtWghDcVZnwQoSi6Np9ITwqN7+sqLiSsM9y4kRE= +github.com/kubernetes-csi/drivers v1.0.2 h1:kaEAMfo+W5YFr23yedBIY+NGnNjr6/PbPzx7N4GYgiQ= +github.com/kubernetes-csi/drivers v1.0.2/go.mod h1:V6rHbbSLCZGaQoIZ8MkyDtoXtcKXZM0F7N3bkloDCOY= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/linkdata/deadlock v0.5.5 h1:d6O+rzEqasSfamGDA8u7bjtaq7hOX8Ha4Zn36Wxrkvo= @@ -557,6 +567,8 @@ github.com/lufia/plan9stats v0.0.0-20260216142805-b3301c5f2a88/go.mod h1:autxFIv github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ= @@ -630,9 +642,11 @@ github.com/nyaruka/phonenumbers v1.4.0/go.mod h1:gv+CtldaFz+G3vHHnasBSirAi3O2XLq github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.145.0 h1:7rdLY2Ewa1WVnjMfJTEKwQ5uPDHYeA1tqNPNROi957U= @@ -1181,6 +1195,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI= +google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1217,16 +1233,26 @@ howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ= k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4= +k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80= +k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ= +k8s.io/apimachinery v0.36.0/go.mod h1:FklypaRJt6n5wUIwWXIP6GJlIpUizTgfo1T/As+Tyxc= k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg= k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c= +k8s.io/client-go v0.36.0 h1:pOYi7C4RHChYjMiHpZSpSbIM6ZxVbRXBy7CuiIwqA3c= +k8s.io/client-go v0.36.0/go.mod h1:ZKKcpwF0aLYfkHFCjillCKaTK/yBkEDHTDXCFY6AS9Y= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a h1:xCeOEAOoGYl2jnJoHkC3hkbPJgdATINPMAxaynU2Ovg= +k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a/go.mod h1:uGBT7iTA6c6MvqUvSXIaYZo9ukscABYi2btjhvgKGZ0= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= +k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/mcs-api v0.4.1 h1:rUygPnCZVS5xiZCzAi54Ngs9on6UQr7MNfx4uJXR2kA= @@ -1235,5 +1261,7 @@ sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= +sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/k8s/sqpkg/csi_driver/BUILD.bazel b/k8s/sqpkg/csi_driver/BUILD.bazel new file mode 100644 index 0000000..b5400ca --- /dev/null +++ b/k8s/sqpkg/csi_driver/BUILD.bazel @@ -0,0 +1,26 @@ +load("//bazel:go.bzl", "go_multi_library") + +go_multi_library( + name = "csi_driver", + srcs = [ + "controller.go", + "identity.go", + "node.go", + "s3.go", + ], + importpath = "go.fuhry.dev/runtime/k8s/sqpkg/csi_driver", + visibility = ["//visibility:public"], + deps = [ + "//constants", + "//utils/context", + "//utils/option", + "@com_github_container_storage_interface_spec//lib/go/csi", + "@com_github_minio_minio_go_v7//:minio-go", + "@com_github_minio_minio_go_v7//pkg/credentials", + "@io_k8s_klog_v2//:klog", + "@org_golang_google_grpc//codes", + "@org_golang_google_grpc//status", + "@org_golang_google_protobuf//encoding/prototext", + "@org_golang_google_protobuf//types/known/wrapperspb", + ], +) diff --git a/k8s/sqpkg/csi_driver/controller.go b/k8s/sqpkg/csi_driver/controller.go new file mode 100644 index 0000000..1595bcb --- /dev/null +++ b/k8s/sqpkg/csi_driver/controller.go @@ -0,0 +1,100 @@ +package csi_driver + +import ( + "github.com/container-storage-interface/spec/lib/go/csi" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "go.fuhry.dev/runtime/utils/context" +) + +type ControllerServer struct { + csi.UnimplementedControllerServer + + volumes map[string]*csi.Volume +} + +// CreateVolume provisions a new volume +func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) { + if req.GetName() == "" { + return nil, status.Error(codes.InvalidArgument, "Volume name missing") + } + + // Check if volume already exists + if vol, exists := cs.volumes[req.GetName()]; exists { + return &csi.CreateVolumeResponse{Volume: vol}, nil + } + + // Get requested size (default to 1GB if not specified) + size := int64(1 * 1024 * 1024 * 1024) + if req.GetCapacityRange() != nil { + size = req.GetCapacityRange().GetRequiredBytes() + } + + // Create the volume + volume := &csi.Volume{ + VolumeId: req.GetName(), + CapacityBytes: size, + VolumeContext: req.GetParameters(), + } + + cs.volumes[req.GetName()] = volume + + return &csi.CreateVolumeResponse{Volume: volume}, nil +} + +// DeleteVolume removes a volume +func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) (*csi.DeleteVolumeResponse, error) { + if req.GetVolumeId() == "" { + return nil, status.Error(codes.InvalidArgument, "Volume ID missing") + } + + delete(cs.volumes, req.GetVolumeId()) + return &csi.DeleteVolumeResponse{}, nil +} + +// ControllerPublishVolume attaches a volume to a node +func (cs *ControllerServer) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { + return &csi.ControllerPublishVolumeResponse{}, nil +} + +// ControllerUnpublishVolume detaches a volume from a node +func (cs *ControllerServer) ControllerUnpublishVolume(ctx context.Context, req *csi.ControllerUnpublishVolumeRequest) (*csi.ControllerUnpublishVolumeResponse, error) { + return &csi.ControllerUnpublishVolumeResponse{}, nil +} + +// ValidateVolumeCapabilities checks if a volume supports requested capabilities +func (cs *ControllerServer) ValidateVolumeCapabilities(ctx context.Context, req *csi.ValidateVolumeCapabilitiesRequest) (*csi.ValidateVolumeCapabilitiesResponse, error) { + if req.GetVolumeId() == "" { + return nil, status.Error(codes.InvalidArgument, "Volume ID missing") + } + + if _, exists := cs.volumes[req.GetVolumeId()]; !exists { + return nil, status.Error(codes.NotFound, "Volume not found") + } + + return &csi.ValidateVolumeCapabilitiesResponse{ + Confirmed: &csi.ValidateVolumeCapabilitiesResponse_Confirmed{ + VolumeCapabilities: req.GetVolumeCapabilities(), + }, + }, nil +} + +// ControllerGetCapabilities reports what operations the controller supports +func (cs *ControllerServer) ControllerGetCapabilities(ctx context.Context, req *csi.ControllerGetCapabilitiesRequest) (*csi.ControllerGetCapabilitiesResponse, error) { + return &csi.ControllerGetCapabilitiesResponse{ + Capabilities: []*csi.ControllerServiceCapability{ + { + Type: &csi.ControllerServiceCapability_Rpc{ + Rpc: &csi.ControllerServiceCapability_RPC{ + Type: csi.ControllerServiceCapability_RPC_CREATE_DELETE_VOLUME, + }, + }, + }, + }, + }, nil +} + +func NewControllerServer() csi.ControllerServer { + return &ControllerServer{} +} diff --git a/k8s/sqpkg/csi_driver/identity.go b/k8s/sqpkg/csi_driver/identity.go new file mode 100644 index 0000000..b8e6ea2 --- /dev/null +++ b/k8s/sqpkg/csi_driver/identity.go @@ -0,0 +1,69 @@ +package csi_driver + +import ( + "fmt" + + "k8s.io/klog/v2" + + "go.fuhry.dev/runtime/constants" + "go.fuhry.dev/runtime/utils/context" + "go.fuhry.dev/runtime/utils/option" + + "github.com/container-storage-interface/spec/lib/go/csi" + "google.golang.org/protobuf/types/known/wrapperspb" +) + +type IdentityServer struct { + csi.UnimplementedIdentityServer + driverName string +} + +func (ids *IdentityServer) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) { + return &csi.GetPluginInfoResponse{ + Name: ids.driverName, + VendorVersion: "v" + constants.Version, + }, nil +} + +func (ids *IdentityServer) GetPluginCapabilities(ctx context.Context, req *csi.GetPluginCapabilitiesRequest) (*csi.GetPluginCapabilitiesResponse, error) { + return &csi.GetPluginCapabilitiesResponse{ + Capabilities: []*csi.PluginCapability{ + { + Type: &csi.PluginCapability_Service_{ + Service: &csi.PluginCapability_Service{ + Type: csi.PluginCapability_Service_CONTROLLER_SERVICE, + }, + }, + }, + }, + }, nil +} + +func (ids *IdentityServer) Probe(ctx context.Context, req *csi.ProbeRequest) (*csi.ProbeResponse, error) { + return &csi.ProbeResponse{ + Ready: wrapperspb.Bool(true), + }, nil +} + +type IdentityServerOption = option.Option[*IdentityServer] + +func WithDriverName(driverName string) IdentityServerOption { + return option.NewOption(func(ids *IdentityServer) error { + ids.driverName = driverName + return nil + }) +} + +func NewIdentityServer(options ...IdentityServerOption) csi.IdentityServer { + ids := &IdentityServer{ + driverName: fmt.Sprintf("%s-csi-sqpkg", constants.OrgSlug), + } + + for _, o := range options { + if err := o.Apply(ids); err != nil { + klog.Fatalf("failed applying identity server option: %v", err) + } + } + + return ids +} diff --git a/k8s/sqpkg/csi_driver/node.go b/k8s/sqpkg/csi_driver/node.go new file mode 100644 index 0000000..32c10c9 --- /dev/null +++ b/k8s/sqpkg/csi_driver/node.go @@ -0,0 +1,248 @@ +package csi_driver + +import ( + "os" + "os/exec" + "path" + "strings" + "sync" + + "k8s.io/klog/v2" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/encoding/prototext" + + "go.fuhry.dev/runtime/constants" + "go.fuhry.dev/runtime/utils/context" + "go.fuhry.dev/runtime/utils/option" + + "github.com/container-storage-interface/spec/lib/go/csi" +) + +var ( + kPackageName = "sqpkg.csi.k8s." + constants.RootDomain + "/package" + kShaSum = "sqpkg.csi.k8s." + constants.RootDomain + "/build-id" +) + +type s3Params struct { + endpointUrl, bucketName, accessKey, secretKey string +} + +type imageRef struct { + pkg, sha string +} + +type NodeServer struct { + csi.UnimplementedNodeServer + + nodeID string + cacheDir string + + volumes map[string]imageRef + refCounts map[imageRef]uint + refCountsLock sync.RWMutex + + *s3Params +} +type NodeServerOption = option.Option[*NodeServer] + +func (r imageRef) imagePath() string { + return path.Join(r.pkg, r.sha[:2], r.sha[2:4], r.sha) +} + +func (ns *NodeServer) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) { + return &csi.NodeGetInfoResponse{ + NodeId: ns.nodeID, + }, nil +} + +func (ns *NodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) { + msg, _ := prototext.Marshal(req) + klog.Infof("stage volume: %s", string(msg)) + return &csi.NodeStageVolumeResponse{}, nil +} + +func (ns *NodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) { + msg, _ := prototext.Marshal(req) + klog.Infof("unstage volume: %s", string(msg)) + return &csi.NodeUnstageVolumeResponse{}, nil +} + +func (ns *NodeServer) increment(pkg, sha, mountPath string) { + ns.refCountsLock.Lock() + defer ns.refCountsLock.Unlock() + + ref := imageRef{pkg, sha} + ns.volumes[mountPath] = ref + if _, ok := ns.refCounts[ref]; !ok { + ns.refCounts[ref] = 0 + } + + ns.refCounts[ref]++ +} + +func (ns *NodeServer) decrement(mountPath string) { + ns.refCountsLock.Lock() + defer ns.refCountsLock.Unlock() + + ref, ok := ns.volumes[mountPath] + if !ok { + return + } + + if _, ok := ns.refCounts[ref]; ok { + ns.refCounts[ref]-- + if ns.refCounts[ref] == 0 { + delete(ns.refCounts, ref) + os.Remove(path.Join(ns.cacheDir, ref.imagePath())) + } + } + + delete(ns.volumes, mountPath) +} + +func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) { + msg, _ := prototext.Marshal(req) + klog.Infof("publish volume: %s", string(msg)) + + targetPath := req.GetTargetPath() + if isSquashFsMounted(targetPath) { + return &csi.NodePublishVolumeResponse{}, nil + } + + var pkg, shasum string + for ctxKey, value := range req.GetVolumeContext() { + switch ctxKey { + case kPackageName: + pkg = value + case kShaSum: + shasum = value + } + } + + if pkg == "" || shasum == "" { + return nil, status.Errorf( + codes.InvalidArgument, + "missing one or more keys: %q, %q", kPackageName, kShaSum) + } + + sqfs, err := ns.maybeGetPackage(ctx, pkg, shasum) + if err != nil { + return nil, status.Errorf( + codes.Internal, + "error loading package %s/%s: %v", pkg, shasum, err) + } + + if err := os.MkdirAll(targetPath, 0o755); err != nil { + return nil, status.Errorf(codes.Internal, "Failed to create target directory: %v", err) + } + + mountArgs := []string{ + "/usr/bin/mount", + "-t", "squashfs", + "-o", "ro,loop", + sqfs, + targetPath, + } + + klog.Infof("running command: %s", strings.Join(mountArgs, " ")) + + cmd := exec.Command(mountArgs[0], mountArgs[1:]...) + if err := cmd.Run(); err != nil { + return nil, status.Errorf( + codes.Internal, + "error running command %q: %v", strings.Join(mountArgs, " "), err) + } + + ns.increment(pkg, shasum, targetPath) + + return &csi.NodePublishVolumeResponse{}, nil +} + +// NodeUnpublishVolume unmounts the volume from the target path +func (ns *NodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) { + msg, _ := prototext.Marshal(req) + klog.Infof("unpublish volume: %s", string(msg)) + + targetPath := req.GetTargetPath() + if !isSquashFsMounted(targetPath) { + return &csi.NodeUnpublishVolumeResponse{}, nil + } + + if err := exec.Command("umount", targetPath).Run(); err != nil { + // handle any errors + return nil, status.Errorf( + codes.Internal, + "failed to unmount volume from %q: %v", targetPath, err) + } + + os.RemoveAll(targetPath) + ns.decrement(targetPath) + + return &csi.NodeUnpublishVolumeResponse{}, nil +} + +func (ns *NodeServer) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) { + return &csi.NodeGetCapabilitiesResponse{ + Capabilities: []*csi.NodeServiceCapability{ + { + Type: &csi.NodeServiceCapability_Rpc{ + Rpc: &csi.NodeServiceCapability_RPC{ + Type: csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME, + }, + }, + }, + }, + }, nil +} + +func WithCacheDir(cacheDir string) NodeServerOption { + return option.NewOption(func(s *NodeServer) error { + err := os.MkdirAll(cacheDir, os.FileMode(0o700)) + if err != nil && !os.IsExist(err) { + return err + } + s.cacheDir = cacheDir + + return nil + }) +} + +func WithS3Params(endpointUrl, bucketName string) NodeServerOption { + return option.NewOption(func(s *NodeServer) error { + if s.s3Params == nil { + s.s3Params = &s3Params{} + } + + s.s3Params.endpointUrl = endpointUrl + s.s3Params.bucketName = bucketName + return nil + }) +} + +func WithS3Credentials(accessKey, secretKey string) NodeServerOption { + return option.NewOption(func(s *NodeServer) error { + if s.s3Params == nil { + s.s3Params = &s3Params{} + } + + s.s3Params.accessKey = accessKey + s.s3Params.secretKey = secretKey + return nil + }) +} + +func NewNodeServer(nodeID string, options ...NodeServerOption) csi.NodeServer { + s := &NodeServer{ + nodeID: nodeID, + volumes: make(map[string]imageRef), + refCounts: make(map[imageRef]uint), + } + for _, o := range options { + if err := o.Apply(s); err != nil { + klog.Fatalf("failed to apply option: %v", err) + } + } + return s +} diff --git a/k8s/sqpkg/csi_driver/s3.go b/k8s/sqpkg/csi_driver/s3.go new file mode 100644 index 0000000..f1bfbcc --- /dev/null +++ b/k8s/sqpkg/csi_driver/s3.go @@ -0,0 +1,141 @@ +package csi_driver + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "hash" + "io" + "os" + "path" + "regexp" + "strings" + + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "k8s.io/klog/v2" +) + +var ( + ValidPackageName = regexp.MustCompile(`^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$`) + ValidShaSum = regexp.MustCompile(`^[0-9a-f]{64}$`) +) + +func (ns *NodeServer) maybeGetPackage(ctx context.Context, packageName, shasum string) (string, error) { + if !ValidPackageName.MatchString(packageName) { + return "", fmt.Errorf("packageName is not valid: %q", packageName) + } + + if !ValidShaSum.MatchString(shasum) { + return "", fmt.Errorf("shasum is not valid: %q", shasum) + } + + shaBytes, err := hex.DecodeString(shasum) + if err != nil { + return "", err + } + + cachePath := path.Join( + ns.cacheDir, + packageName, + shasum[:2], + shasum[2:4], + shasum, + ) + klog.V(1).Infof("maybeGetPackage: cachePath: %q", cachePath) + + fp, err := os.OpenFile(cachePath, os.O_RDONLY, os.FileMode(0)) + if err != nil && os.IsNotExist(err) { + klog.V(1).Infof(" -> cachePath does not exist, attempting to download") + if ns.s3Params == nil { + return "", errors.New("s3Params not populated") + } + + var creds *credentials.Credentials + + if ns.s3Params.accessKey != "" && ns.s3Params.secretKey != "" { + creds = credentials.NewStaticV4(ns.s3Params.accessKey, ns.s3Params.secretKey, "") + } else { + creds = credentials.NewEnvAWS() + } + + cc := &credentials.CredContext{ + Endpoint: ns.s3Params.endpointUrl, + } + + if _, err := creds.GetWithContext(cc); err != nil { + return "", errors.New("cannot load credentials") + } + + client, err := minio.New(ns.s3Params.endpointUrl, &minio.Options{ + Creds: creds, + Secure: true, + }) + if err != nil { + return "", err + } + + err = os.MkdirAll(path.Dir(cachePath), os.FileMode(0o700)) + if err != nil && !os.IsExist(err) { + return "", err + } + + packagePath := path.Join(packageName, shasum[:2], shasum[2:4], shasum) + + err = client.FGetObject( + ctx, + ns.s3Params.bucketName, + packagePath, + cachePath, + minio.GetObjectOptions{}) + if err != nil { + return "", err + } + } else if err != nil { + return "", err + } else if fp != nil { + fp.Close() + } + + fp, err = os.OpenFile(cachePath, os.O_RDONLY, os.FileMode(0o600)) + if err != nil { + return "", err + } + defer fp.Close() + var hasher hash.Hash = sha256.New() + io.Copy(hasher, fp) + hashBuf := make([]byte, 0) + hashBuf = hasher.Sum(hashBuf) + + if !bytes.Equal(hashBuf, shaBytes) { + return "", fmt.Errorf( + "package at path %q does not match expected checksum:\n expected: %s\n actual: %s", + cachePath, shasum, hex.EncodeToString(hashBuf)) + } + + return cachePath, nil +} + +func isSquashFsMounted(mountPath string) bool { + mountsBytes, err := os.ReadFile("/proc/mounts") + if err != nil { + return false + } + + mounts := strings.Split(strings.TrimSpace(string(mountsBytes)), "\n") + for _, line := range mounts { + parts := strings.SplitN(line, " ", 6) + if len(parts) >= 3 { + if strings.HasPrefix(parts[0], "/dev/loop") && + parts[1] == mountPath && + parts[2] == "squashfs" { + return true + } + } + } + + return false +} diff --git a/thirdparty/kubernetes/csi_common_0001-add-unimplemented-server-types.patch b/thirdparty/kubernetes/csi_common_0001-add-unimplemented-server-types.patch new file mode 100644 index 0000000..b6e11ca --- /dev/null +++ b/thirdparty/kubernetes/csi_common_0001-add-unimplemented-server-types.patch @@ -0,0 +1,39 @@ +diff --git a/pkg/csi-common/controllerserver-default.go b/pkg/csi-common/controllerserver-default.go +index db72dc2e..bc79f74f 100644 +--- a/pkg/csi-common/controllerserver-default.go ++++ b/pkg/csi-common/controllerserver-default.go +@@ -25,6 +25,8 @@ import ( + ) + + type DefaultControllerServer struct { ++ csi.UnimplementedControllerServer ++ + Driver *CSIDriver + } + +diff --git a/pkg/csi-common/identityserver-default.go b/pkg/csi-common/identityserver-default.go +index b16b67c0..252b37c8 100644 +--- a/pkg/csi-common/identityserver-default.go ++++ b/pkg/csi-common/identityserver-default.go +@@ -25,6 +25,8 @@ import ( + ) + + type DefaultIdentityServer struct { ++ csi.UnimplementedIdentityServer ++ + Driver *CSIDriver + } + +diff --git a/pkg/csi-common/nodeserver-default.go b/pkg/csi-common/nodeserver-default.go +index cd2355bf..f615664e 100644 +--- a/pkg/csi-common/nodeserver-default.go ++++ b/pkg/csi-common/nodeserver-default.go +@@ -25,6 +25,8 @@ import ( + ) + + type DefaultNodeServer struct { ++ csi.UnimplementedNodeServer ++ + Driver *CSIDriver + } + -- 2.52.0