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.
],
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",
"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",
"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",
--- /dev/null
+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"],
+)
--- /dev/null
+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()
+}
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
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
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
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
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
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
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=
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=
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=
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=
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=
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=
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=
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=
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=
--- /dev/null
+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",
+ ],
+)
--- /dev/null
+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{}
+}
--- /dev/null
+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
+}
--- /dev/null
+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
+}
--- /dev/null
+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
+}
--- /dev/null
+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
+ }
+