From a693cbad370b5b9905308d26791894d888c65507 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Tue, 1 Nov 2022 14:26:54 +0100 Subject: [PATCH] added basic plugin structure --- README.md | 10 ++ cmd/char/ls.go | 40 ++++++++ cmd/char/root.go | 15 +++ examples/basic/.char/plugins/gocli | 1 + examples/basic/test.sh | 3 + go.mod | 27 ++++++ go.sum | 87 +++++++++++++++++ go.work | 3 + go.work.sum | 1 + main.go | 13 +++ pkg/plugins/script/script.go | 1 + pkg/register/plugin.go | 5 + pkg/register/plugin_api.go | 19 ++++ pkg/register/plugin_builder.go | 48 ++++++++++ pkg/register/plugin_client.go | 22 +++++ pkg/register/plugin_register.go | 147 +++++++++++++++++++++++++++++ pkg/register/plugin_server.go | 10 ++ plugins/gocli/main.go | 29 ++++++ 18 files changed, 481 insertions(+) create mode 100644 README.md create mode 100644 cmd/char/ls.go create mode 100644 cmd/char/root.go create mode 120000 examples/basic/.char/plugins/gocli create mode 100755 examples/basic/test.sh create mode 100644 go.mod create mode 100644 go.sum create mode 100644 go.work create mode 100644 go.work.sum create mode 100644 main.go create mode 100644 pkg/plugins/script/script.go create mode 100644 pkg/register/plugin.go create mode 100644 pkg/register/plugin_api.go create mode 100644 pkg/register/plugin_builder.go create mode 100644 pkg/register/plugin_client.go create mode 100644 pkg/register/plugin_register.go create mode 100644 pkg/register/plugin_server.go create mode 100644 plugins/gocli/main.go diff --git a/README.md b/README.md new file mode 100644 index 0000000..51394e9 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# Char + +```yaml +# file: .char.yml +registry: git.front.kjuulh.io +plugins: + "kjuulh/char#/plugins/gocli": + vars: + name: "char" +``` diff --git a/cmd/char/ls.go b/cmd/char/ls.go new file mode 100644 index 0000000..6da075f --- /dev/null +++ b/cmd/char/ls.go @@ -0,0 +1,40 @@ +package char + +import ( + "fmt" + + "git.front.kjuulh.io/kjuulh/char/pkg/register" + "github.com/spf13/cobra" +) + +func NewLsCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "ls", + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + + r, err := register. + NewPluginRegisterBuilder(). + Add("gocli", ""). + Build(ctx) + if err != nil { + return err + } + + about, err := r.About(ctx) + if err != nil { + return err + } + + for plugin, aboutText := range about { + fmt.Printf("plugin: %s\n", plugin) + fmt.Printf("\tabout: %s\n", aboutText) + fmt.Println() + } + + return nil + }, + } + + return cmd +} diff --git a/cmd/char/root.go b/cmd/char/root.go new file mode 100644 index 0000000..aff6551 --- /dev/null +++ b/cmd/char/root.go @@ -0,0 +1,15 @@ +package char + +import "github.com/spf13/cobra" + +func NewCharCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "char", + } + + cmd.AddCommand( + NewLsCommand(), + ) + + return cmd +} diff --git a/examples/basic/.char/plugins/gocli b/examples/basic/.char/plugins/gocli new file mode 120000 index 0000000..11a54ed --- /dev/null +++ b/examples/basic/.char/plugins/gocli @@ -0,0 +1 @@ +../../../../ \ No newline at end of file diff --git a/examples/basic/test.sh b/examples/basic/test.sh new file mode 100755 index 0000000..d133f8e --- /dev/null +++ b/examples/basic/test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +go run ../../main.go ls diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f1f4b7d --- /dev/null +++ b/go.mod @@ -0,0 +1,27 @@ +module git.front.kjuulh.io/kjuulh/char + +go 1.19 + +require ( + github.com/hashicorp/go-hclog v0.14.1 + github.com/hashicorp/go-plugin v1.4.5 + github.com/spf13/cobra v1.6.1 + golang.org/x/sync v0.0.0-20190423024810-112230192c58 +) + +require ( + github.com/fatih/color v1.7.0 // indirect + github.com/golang/protobuf v1.3.4 // indirect + github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect + github.com/inconshreveable/mousetrap v1.0.1 // indirect + github.com/mattn/go-colorable v0.1.4 // indirect + github.com/mattn/go-isatty v0.0.10 // indirect + github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 // indirect + github.com/oklog/run v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect + golang.org/x/sys v0.0.0-20191008105621-543471e840be // indirect + golang.org/x/text v0.3.0 // indirect + google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect + google.golang.org/grpc v1.27.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e7d7e5b --- /dev/null +++ b/go.sum @@ -0,0 +1,87 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/hashicorp/go-hclog v0.14.1 h1:nQcJDQwIAGnmoUWp8ubocEX40cCml/17YkF6csQLReU= +github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-plugin v1.4.5 h1:oTE/oQR4eghggRg8VY7PAz3dr++VwDNBGCcOfIvHpBo= +github.com/hashicorp/go-plugin v1.4.5/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77 h1:7GoSOOW2jpsfkntVKaS2rAr1TJqfcxotyaUcuxoZSzg= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191008105621-543471e840be h1:QAcqgptGM8IQBC9K/RC4o+O9YmqEm0diQn9QmZw/0mU= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/go.work b/go.work new file mode 100644 index 0000000..52a3b7c --- /dev/null +++ b/go.work @@ -0,0 +1,3 @@ +go 1.19 + +use . diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..55be116 --- /dev/null +++ b/go.work.sum @@ -0,0 +1 @@ +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= diff --git a/main.go b/main.go new file mode 100644 index 0000000..530284b --- /dev/null +++ b/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "log" + + "git.front.kjuulh.io/kjuulh/char/cmd/char" +) + +func main() { + if err := char.NewCharCmd().Execute(); err != nil { + log.Fatal(err) + } +} diff --git a/pkg/plugins/script/script.go b/pkg/plugins/script/script.go new file mode 100644 index 0000000..4759160 --- /dev/null +++ b/pkg/plugins/script/script.go @@ -0,0 +1 @@ +package script diff --git a/pkg/register/plugin.go b/pkg/register/plugin.go new file mode 100644 index 0000000..139dd36 --- /dev/null +++ b/pkg/register/plugin.go @@ -0,0 +1,5 @@ +package register + +type Plugin interface { + About() string +} diff --git a/pkg/register/plugin_api.go b/pkg/register/plugin_api.go new file mode 100644 index 0000000..b19ada3 --- /dev/null +++ b/pkg/register/plugin_api.go @@ -0,0 +1,19 @@ +package register + +import ( + "net/rpc" + + "github.com/hashicorp/go-plugin" +) + +type PluginAPI struct { + Impl Plugin +} + +func (pa *PluginAPI) Server(*plugin.MuxBroker) (any, error) { + return &PluginServer{Impl: pa.Impl}, nil +} + +func (*PluginAPI) Client(b *plugin.MuxBroker, c *rpc.Client) (any, error) { + return &PluginClient{client: c}, nil +} diff --git a/pkg/register/plugin_builder.go b/pkg/register/plugin_builder.go new file mode 100644 index 0000000..18735d2 --- /dev/null +++ b/pkg/register/plugin_builder.go @@ -0,0 +1,48 @@ +package register + +import ( + "context" + "os" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-plugin" +) + +type PluginBuilder struct { + serveConfig *plugin.ServeConfig +} + +func NewPluginBuilder(name string, p Plugin) *PluginBuilder { + logger := hclog.New(&hclog.LoggerOptions{ + Level: hclog.Trace, + Output: os.Stderr, + JSONFormat: false, + }) + + var pluginMap = map[string]plugin.Plugin{ + name: &PluginAPI{ + Impl: p, + }, + } + + serveConfig := &plugin.ServeConfig{ + HandshakeConfig: plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "BASIC_PLUGIN", + MagicCookieValue: "char", + }, + Plugins: pluginMap, + Logger: logger, + } + + return &PluginBuilder{ + serveConfig: serveConfig, + } +} + +func (pr *PluginBuilder) Serve(ctx context.Context) error { + plugin.Serve( + pr.serveConfig, + ) + return nil +} diff --git a/pkg/register/plugin_client.go b/pkg/register/plugin_client.go new file mode 100644 index 0000000..0477b08 --- /dev/null +++ b/pkg/register/plugin_client.go @@ -0,0 +1,22 @@ +package register + +import ( + "log" + "net/rpc" +) + +type PluginClient struct { + client *rpc.Client +} + +var _ Plugin = &PluginClient{} + +func (pc *PluginClient) About() string { + var resp string + err := pc.client.Call("Plugin.About", new(any), &resp) + if err != nil { + log.Fatal(err) + } + + return resp +} diff --git a/pkg/register/plugin_register.go b/pkg/register/plugin_register.go new file mode 100644 index 0000000..875ff02 --- /dev/null +++ b/pkg/register/plugin_register.go @@ -0,0 +1,147 @@ +package register + +import ( + "context" + "errors" + "fmt" + "os" + "os/exec" + + "github.com/hashicorp/go-hclog" + "github.com/hashicorp/go-plugin" + "golang.org/x/sync/errgroup" +) + +type PluginRegisterBuilder struct { + plugins map[string]PluginAPI +} + +func NewPluginRegisterBuilder() *PluginRegisterBuilder { + return &PluginRegisterBuilder{} +} + +func (pr *PluginRegisterBuilder) Add(name, path string) *PluginRegisterBuilder { + pr.plugins[name] = PluginAPI{} + + return pr +} + +func (pr *PluginRegisterBuilder) Build(ctx context.Context) (*PluginRegister, error) { + clients := make(map[string]*pluginClientWrapper, 0) + errgroup, _ := errgroup.WithContext(ctx) + + for name, p := range pr.plugins { + name, p := name, p + + errgroup.Go(func() error { + client := plugin.NewClient(&plugin.ClientConfig{ + HandshakeConfig: plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "BASIC_PLUGIN", + MagicCookieValue: "char", + }, + Logger: hclog.New(&hclog.LoggerOptions{ + Name: "char", + Output: os.Stdout, + Level: hclog.Debug, + }), + Cmd: exec.Command("sh", "-c", fmt.Sprintf( + "(cd ./.char/plugins/%s; go run plugins/%s/main.go)", + name, + name, + )), + Plugins: map[string]plugin.Plugin{ + name: &p, + }, + }) + + rpcClient, err := client.Client() + if err != nil { + return err + } + + raw, err := rpcClient.Dispense(name) + if err != nil { + return err + } + + pluginApi, ok := raw.(Plugin) + if !ok { + return errors.New("could not cast as plugin") + } + + clients[name] = &pluginClientWrapper{ + plugin: pluginApi, + client: client, + } + + return nil + }) + } + + err := errgroup.Wait() + if err != nil { + return nil, err + } + + return &PluginRegister{ + clients: clients, + }, nil +} + +// --- + +type pluginClientWrapper struct { + plugin Plugin + client *plugin.Client +} + +func (pcw *pluginClientWrapper) Close() { + pcw.client.Kill() +} + +// --- + +type PluginRegister struct { + clients map[string]*pluginClientWrapper +} + +func (pr *PluginRegister) Close() error { + errgroup, _ := errgroup.WithContext(context.Background()) + + for _, c := range pr.clients { + c := c + + errgroup.Go(func() error { + c.Close() + return nil + }) + } + + if err := errgroup.Wait(); err != nil { + return err + } + return nil +} + +func (pr *PluginRegister) About(ctx context.Context) (map[string]string, error) { + list := make(map[string]string, len(pr.clients)) + + errgroup, ctx := errgroup.WithContext(ctx) + + for n, c := range pr.clients { + n, c := n, c + errgroup.Go(func() error { + about := c.plugin.About() + + list[n] = about + return nil + }) + } + + if err := errgroup.Wait(); err != nil { + return nil, err + } + + return list, nil +} diff --git a/pkg/register/plugin_server.go b/pkg/register/plugin_server.go new file mode 100644 index 0000000..bdb333f --- /dev/null +++ b/pkg/register/plugin_server.go @@ -0,0 +1,10 @@ +package register + +type PluginServer struct { + Impl Plugin +} + +func (ps *PluginServer) About(args any, resp *string) error { + *resp = ps.Impl.About() + return nil +} diff --git a/plugins/gocli/main.go b/plugins/gocli/main.go new file mode 100644 index 0000000..e48e79f --- /dev/null +++ b/plugins/gocli/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "context" + "log" + + "git.front.kjuulh.io/kjuulh/char/pkg/register" +) + +type GoCliPlugin struct { +} + +// About implements register.Plugin +func (*GoCliPlugin) About() string { + return "gocli" +} + +var _ register.Plugin = &GoCliPlugin{} + +func main() { + if err := register. + NewPluginBuilder( + "gocli", + &GoCliPlugin{}, + ). + Serve(context.Background()); err != nil { + log.Fatal(err) + } +}