initial commit
This commit is contained in:
parent
0c44f26522
commit
2b614f6bd3
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) <year> <copyright holders>
|
||||
Copyright (c) 2022 strNophix
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
6
Makefile
Normal file
6
Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
.PHONY: protos
|
||||
|
||||
protos:
|
||||
protoc --go_out=. --go_opt=paths=source_relative \
|
||||
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
|
||||
proto/gocron-server.proto
|
26
cmd/config.go
Normal file
26
cmd/config.go
Normal file
@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
gocron_server "github.com/strnophix/gocron-server/pkg"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
Host string `toml:"host"`
|
||||
}
|
||||
|
||||
type UnitConfig struct {
|
||||
Name string `toml:"name"`
|
||||
Command string `toml:"command"`
|
||||
Cron string `toml:"cron"`
|
||||
}
|
||||
|
||||
func (u *UnitConfig) ToSchedulerUnit() *gocron_server.SchedulerUnit {
|
||||
cmd := gocron_server.NewUnitExecCmd(u.Command)
|
||||
unit := gocron_server.NewSchedulerUnit(u.Name, u.Cron, cmd)
|
||||
return unit
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig `toml:"server"`
|
||||
Unit []UnitConfig `toml:"unit"`
|
||||
}
|
56
cmd/main.go
Normal file
56
cmd/main.go
Normal file
@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
gocron_server "github.com/strnophix/gocron-server/pkg"
|
||||
pb "github.com/strnophix/gocron-server/pkg/proto"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatalln("Expected a gocron-server config as the first argument.")
|
||||
}
|
||||
|
||||
cfgPath := os.Args[1]
|
||||
cfg := Config{}
|
||||
_, err := toml.DecodeFile(cfgPath, &cfg)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
l, err := net.Listen("tcp", cfg.Server.Host)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start server on %s: %v", cfg.Server.Host, err)
|
||||
}
|
||||
|
||||
log.Printf("gocron-server is running on %s\n", cfg.Server.Host)
|
||||
|
||||
gs := grpc.NewServer()
|
||||
|
||||
s := gocron_server.NewSchedulerService()
|
||||
defer s.Shutdown()
|
||||
|
||||
for _, unitCfg := range cfg.Unit {
|
||||
unit := unitCfg.ToSchedulerUnit()
|
||||
err = s.AddUnit(unit)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
log.Printf("Succesfully added unit %s\n", unit.Name)
|
||||
}
|
||||
|
||||
pb.RegisterSchedulerServer(gs, s)
|
||||
reflection.Register(gs)
|
||||
|
||||
if err := gs.Serve(l); err != nil {
|
||||
log.Fatalf("Failed to serve: %v", err)
|
||||
}
|
||||
}
|
12
examples/config.toml
Normal file
12
examples/config.toml
Normal file
@ -0,0 +1,12 @@
|
||||
# An example config that can be supplied to the CLI
|
||||
[server]
|
||||
host=":9092"
|
||||
|
||||
[[unit]]
|
||||
name="echo"
|
||||
command="echo heyooo"
|
||||
cron="* * * * *"
|
||||
|
||||
[[unit]]
|
||||
name="notify"
|
||||
command="notify-send heyooo"
|
57
examples/counter.go
Normal file
57
examples/counter.go
Normal file
@ -0,0 +1,57 @@
|
||||
// Sample usage of the gocron-server pkg
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
gocron_server "github.com/strnophix/gocron-server/pkg"
|
||||
pb "github.com/strnophix/gocron-server/pkg/proto"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection"
|
||||
)
|
||||
|
||||
const (
|
||||
port = ":9092"
|
||||
)
|
||||
|
||||
type Counter struct {
|
||||
Current int
|
||||
}
|
||||
|
||||
func (c *Counter) Increment() (string, error) {
|
||||
c.Current += 1
|
||||
fmt.Printf("Currently: %d\n", c.Current)
|
||||
return fmt.Sprint(c.Current), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
l, err := net.Listen("tcp", port)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to start server on %s: %v", port, err)
|
||||
}
|
||||
|
||||
log.Printf("Running on port %s\n", port)
|
||||
|
||||
c := &Counter{Current: 1}
|
||||
|
||||
gs := grpc.NewServer()
|
||||
|
||||
s := gocron_server.NewSchedulerService()
|
||||
defer s.Shutdown()
|
||||
|
||||
incr := gocron_server.NewUnitExecFn(c.Increment)
|
||||
unit := gocron_server.NewManualUnit("incr", incr)
|
||||
err = s.AddUnit(unit)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
pb.RegisterSchedulerServer(gs, s)
|
||||
reflection.Register(gs)
|
||||
|
||||
if err := gs.Serve(l); err != nil {
|
||||
log.Fatalf("Failed to serve: %v", err)
|
||||
}
|
||||
}
|
21
go.mod
Normal file
21
go.mod
Normal file
@ -0,0 +1,21 @@
|
||||
module github.com/strnophix/gocron-server
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.0.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
|
||||
github.com/go-co-op/gocron v1.11.0 // indirect
|
||||
github.com/golang/protobuf v1.4.3 // indirect
|
||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/urfave/cli/v2 v2.3.0 // indirect
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd // indirect
|
||||
golang.org/x/text v0.3.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
||||
google.golang.org/grpc v1.43.0 // indirect
|
||||
google.golang.org/protobuf v1.25.0 // indirect
|
||||
)
|
129
go.sum
Normal file
129
go.sum
Normal file
@ -0,0 +1,129 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.0.0 h1:dtDWrepsVPfW9H/4y7dDgFc2MBUSeJhlaDtK13CxFlU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-co-op/gocron v1.11.0 h1:ujOMubCpGcTxnnR/9vJIPIEpgwuAjbueAYqJRNr+nHg=
|
||||
github.com/go-co-op/gocron v1.11.0/go.mod h1:qtlsoMpHlSdIZ3E/xuZzrrAbeX3u5JtPvWf2TcdutU0=
|
||||
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.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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-20190108225652-1e06a53dbb7e/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/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
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/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
|
||||
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
|
56
pkg/broadcaster.go
Normal file
56
pkg/broadcaster.go
Normal file
@ -0,0 +1,56 @@
|
||||
package gocron_server
|
||||
|
||||
import (
|
||||
pb "github.com/strnophix/gocron-server/pkg/proto"
|
||||
)
|
||||
|
||||
type Streamer interface {
|
||||
Send(*pb.ListenJobResponse) error
|
||||
}
|
||||
|
||||
type Streams []Streamer
|
||||
|
||||
type EventBroadcaster struct {
|
||||
streams Streams
|
||||
}
|
||||
|
||||
func NewEventBroadcaster() *EventBroadcaster {
|
||||
return &EventBroadcaster{
|
||||
streams: make(Streams, 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *EventBroadcaster) Subscribe(stream Streamer) {
|
||||
b.streams = append(b.streams, stream)
|
||||
}
|
||||
|
||||
func (b *EventBroadcaster) Publish(resp *pb.ListenJobResponse) {
|
||||
closed := make([]int, 0)
|
||||
for idx, stream := range b.streams {
|
||||
if err := stream.Send(resp); err != nil {
|
||||
closed = append(closed, idx)
|
||||
}
|
||||
}
|
||||
|
||||
if len(closed) == len(b.streams) {
|
||||
b.streams = nil
|
||||
return
|
||||
}
|
||||
|
||||
for idx := len(closed) - 1; idx > -1; idx-- {
|
||||
removeStream(b.streams, idx)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *EventBroadcaster) SubscriberCount() int {
|
||||
return len(b.streams)
|
||||
}
|
||||
|
||||
func removeStream(s Streams, i int) Streams {
|
||||
s[i] = s[len(s)-1]
|
||||
return s[:len(s)-1]
|
||||
}
|
||||
|
||||
func NewBroadcastResponse(name, result string) *pb.ListenJobResponse {
|
||||
return &pb.ListenJobResponse{JobName: name, JobResult: result}
|
||||
}
|
43
pkg/broadcaster_test.go
Normal file
43
pkg/broadcaster_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
package gocron_server_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
gocron_server "github.com/strnophix/gocron-server/pkg"
|
||||
pb "github.com/strnophix/gocron-server/pkg/proto"
|
||||
)
|
||||
|
||||
type TestStreamer struct {
|
||||
CalledOnce bool
|
||||
}
|
||||
|
||||
func NewTestStreamer() *TestStreamer {
|
||||
return &TestStreamer{CalledOnce: false}
|
||||
}
|
||||
|
||||
func (ts *TestStreamer) Send(stream *pb.ListenJobResponse) error {
|
||||
if ts.CalledOnce == true {
|
||||
return fmt.Errorf("Send can only be called once")
|
||||
}
|
||||
|
||||
ts.CalledOnce = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPublish(t *testing.T) {
|
||||
cl := NewTestStreamer()
|
||||
eb := gocron_server.NewEventBroadcaster()
|
||||
eb.Subscribe(cl)
|
||||
msg := gocron_server.NewBroadcastResponse("test", "Content :)")
|
||||
eb.Publish(msg)
|
||||
|
||||
if cl.CalledOnce == false {
|
||||
t.Fatalf("The Send function should have been called on the stream")
|
||||
}
|
||||
|
||||
eb.Publish(msg)
|
||||
if eb.SubscriberCount() != 0 {
|
||||
t.Fatalf("The second call of Send should have unsubscribed the stream")
|
||||
}
|
||||
}
|
347
pkg/proto/gocron-server.pb.go
Normal file
347
pkg/proto/gocron-server.pb.go
Normal file
@ -0,0 +1,347 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.19.2
|
||||
// source: proto/gocron-server.proto
|
||||
|
||||
package gocron_server
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type RunJobRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
UnitName string `protobuf:"bytes,1,opt,name=UnitName,proto3" json:"UnitName,omitempty"`
|
||||
RunAt int64 `protobuf:"varint,10,opt,name=RunAt,proto3" json:"RunAt,omitempty"`
|
||||
}
|
||||
|
||||
func (x *RunJobRequest) Reset() {
|
||||
*x = RunJobRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RunJobRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RunJobRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RunJobRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RunJobRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RunJobRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_gocron_server_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *RunJobRequest) GetUnitName() string {
|
||||
if x != nil {
|
||||
return x.UnitName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RunJobRequest) GetRunAt() int64 {
|
||||
if x != nil {
|
||||
return x.RunAt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type RunJobResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *RunJobResponse) Reset() {
|
||||
*x = RunJobResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RunJobResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RunJobResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RunJobResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RunJobResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RunJobResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_gocron_server_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
type ListenJobRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (x *ListenJobRequest) Reset() {
|
||||
*x = ListenJobRequest{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListenJobRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListenJobRequest) ProtoMessage() {}
|
||||
|
||||
func (x *ListenJobRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListenJobRequest.ProtoReflect.Descriptor instead.
|
||||
func (*ListenJobRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_gocron_server_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
type ListenJobResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
JobName string `protobuf:"bytes,1,opt,name=JobName,proto3" json:"JobName,omitempty"`
|
||||
JobResult string `protobuf:"bytes,2,opt,name=JobResult,proto3" json:"JobResult,omitempty"`
|
||||
}
|
||||
|
||||
func (x *ListenJobResponse) Reset() {
|
||||
*x = ListenJobResponse{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ListenJobResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ListenJobResponse) ProtoMessage() {}
|
||||
|
||||
func (x *ListenJobResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_gocron_server_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ListenJobResponse.ProtoReflect.Descriptor instead.
|
||||
func (*ListenJobResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_gocron_server_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *ListenJobResponse) GetJobName() string {
|
||||
if x != nil {
|
||||
return x.JobName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ListenJobResponse) GetJobResult() string {
|
||||
if x != nil {
|
||||
return x.JobResult
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_proto_gocron_server_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_proto_gocron_server_proto_rawDesc = []byte{
|
||||
0x0a, 0x19, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x63, 0x72, 0x6f, 0x6e, 0x2d, 0x73,
|
||||
0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x67, 0x6f, 0x63,
|
||||
0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x22, 0x41, 0x0a, 0x0d, 0x52, 0x75,
|
||||
0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x55,
|
||||
0x6e, 0x69, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x55,
|
||||
0x6e, 0x69, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x52, 0x75, 0x6e, 0x41, 0x74,
|
||||
0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x52, 0x75, 0x6e, 0x41, 0x74, 0x22, 0x10, 0x0a,
|
||||
0x0e, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
|
||||
0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x22, 0x4b, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x4a, 0x6f, 0x62,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x4e,
|
||||
0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x4a, 0x6f, 0x62, 0x4e, 0x61,
|
||||
0x6d, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74,
|
||||
0x32, 0xa5, 0x01, 0x0a, 0x09, 0x53, 0x63, 0x68, 0x65, 0x64, 0x75, 0x6c, 0x65, 0x72, 0x12, 0x45,
|
||||
0x0a, 0x06, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x12, 0x1c, 0x2e, 0x67, 0x6f, 0x63, 0x72, 0x6f,
|
||||
0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x67, 0x6f, 0x63, 0x72, 0x6f, 0x6e, 0x5f,
|
||||
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x52, 0x75, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x4a,
|
||||
0x6f, 0x62, 0x73, 0x12, 0x1f, 0x2e, 0x67, 0x6f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x67, 0x6f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x65,
|
||||
0x72, 0x76, 0x65, 0x72, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x65, 0x6e, 0x4a, 0x6f, 0x62, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68,
|
||||
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x73, 0x74, 0x72, 0x6e, 0x6f, 0x70, 0x68, 0x69, 0x78,
|
||||
0x2f, 0x67, 0x6f, 0x63, 0x72, 0x6f, 0x6e, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x3b, 0x67,
|
||||
0x6f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_proto_gocron_server_proto_rawDescOnce sync.Once
|
||||
file_proto_gocron_server_proto_rawDescData = file_proto_gocron_server_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_proto_gocron_server_proto_rawDescGZIP() []byte {
|
||||
file_proto_gocron_server_proto_rawDescOnce.Do(func() {
|
||||
file_proto_gocron_server_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_gocron_server_proto_rawDescData)
|
||||
})
|
||||
return file_proto_gocron_server_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_gocron_server_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||
var file_proto_gocron_server_proto_goTypes = []interface{}{
|
||||
(*RunJobRequest)(nil), // 0: gocron_server.RunJobRequest
|
||||
(*RunJobResponse)(nil), // 1: gocron_server.RunJobResponse
|
||||
(*ListenJobRequest)(nil), // 2: gocron_server.ListenJobRequest
|
||||
(*ListenJobResponse)(nil), // 3: gocron_server.ListenJobResponse
|
||||
}
|
||||
var file_proto_gocron_server_proto_depIdxs = []int32{
|
||||
0, // 0: gocron_server.Scheduler.RunJob:input_type -> gocron_server.RunJobRequest
|
||||
2, // 1: gocron_server.Scheduler.ListenJobs:input_type -> gocron_server.ListenJobRequest
|
||||
1, // 2: gocron_server.Scheduler.RunJob:output_type -> gocron_server.RunJobResponse
|
||||
3, // 3: gocron_server.Scheduler.ListenJobs:output_type -> gocron_server.ListenJobResponse
|
||||
2, // [2:4] is the sub-list for method output_type
|
||||
0, // [0:2] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_gocron_server_proto_init() }
|
||||
func file_proto_gocron_server_proto_init() {
|
||||
if File_proto_gocron_server_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_proto_gocron_server_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RunJobRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_gocron_server_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*RunJobResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_gocron_server_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListenJobRequest); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_proto_gocron_server_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ListenJobResponse); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_gocron_server_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 4,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_proto_gocron_server_proto_goTypes,
|
||||
DependencyIndexes: file_proto_gocron_server_proto_depIdxs,
|
||||
MessageInfos: file_proto_gocron_server_proto_msgTypes,
|
||||
}.Build()
|
||||
File_proto_gocron_server_proto = out.File
|
||||
file_proto_gocron_server_proto_rawDesc = nil
|
||||
file_proto_gocron_server_proto_goTypes = nil
|
||||
file_proto_gocron_server_proto_depIdxs = nil
|
||||
}
|
23
pkg/proto/gocron-server.proto
Normal file
23
pkg/proto/gocron-server.proto
Normal file
@ -0,0 +1,23 @@
|
||||
syntax = "proto3";
|
||||
package gocron_server;
|
||||
|
||||
option go_package = "github.com/strnophix/gocron-server;gocron_server";
|
||||
|
||||
service Scheduler {
|
||||
rpc RunJob(RunJobRequest) returns (RunJobResponse);
|
||||
rpc ListenJobs(ListenJobRequest) returns (stream ListenJobResponse);
|
||||
}
|
||||
|
||||
message RunJobRequest {
|
||||
string UnitName = 1;
|
||||
int64 RunAt = 10;
|
||||
}
|
||||
|
||||
message RunJobResponse {}
|
||||
|
||||
message ListenJobRequest {}
|
||||
|
||||
message ListenJobResponse {
|
||||
string JobName = 1;
|
||||
string JobResult = 2;
|
||||
}
|
165
pkg/proto/gocron-server_grpc.pb.go
Normal file
165
pkg/proto/gocron-server_grpc.pb.go
Normal file
@ -0,0 +1,165 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
|
||||
package gocron_server
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
// SchedulerClient is the client API for Scheduler service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
type SchedulerClient interface {
|
||||
RunJob(ctx context.Context, in *RunJobRequest, opts ...grpc.CallOption) (*RunJobResponse, error)
|
||||
ListenJobs(ctx context.Context, in *ListenJobRequest, opts ...grpc.CallOption) (Scheduler_ListenJobsClient, error)
|
||||
}
|
||||
|
||||
type schedulerClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewSchedulerClient(cc grpc.ClientConnInterface) SchedulerClient {
|
||||
return &schedulerClient{cc}
|
||||
}
|
||||
|
||||
func (c *schedulerClient) RunJob(ctx context.Context, in *RunJobRequest, opts ...grpc.CallOption) (*RunJobResponse, error) {
|
||||
out := new(RunJobResponse)
|
||||
err := c.cc.Invoke(ctx, "/gocron_server.Scheduler/RunJob", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *schedulerClient) ListenJobs(ctx context.Context, in *ListenJobRequest, opts ...grpc.CallOption) (Scheduler_ListenJobsClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &Scheduler_ServiceDesc.Streams[0], "/gocron_server.Scheduler/ListenJobs", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &schedulerListenJobsClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type Scheduler_ListenJobsClient interface {
|
||||
Recv() (*ListenJobResponse, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type schedulerListenJobsClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *schedulerListenJobsClient) Recv() (*ListenJobResponse, error) {
|
||||
m := new(ListenJobResponse)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// SchedulerServer is the server API for Scheduler service.
|
||||
// All implementations must embed UnimplementedSchedulerServer
|
||||
// for forward compatibility
|
||||
type SchedulerServer interface {
|
||||
RunJob(context.Context, *RunJobRequest) (*RunJobResponse, error)
|
||||
ListenJobs(*ListenJobRequest, Scheduler_ListenJobsServer) error
|
||||
mustEmbedUnimplementedSchedulerServer()
|
||||
}
|
||||
|
||||
// UnimplementedSchedulerServer must be embedded to have forward compatible implementations.
|
||||
type UnimplementedSchedulerServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedSchedulerServer) RunJob(context.Context, *RunJobRequest) (*RunJobResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RunJob not implemented")
|
||||
}
|
||||
func (UnimplementedSchedulerServer) ListenJobs(*ListenJobRequest, Scheduler_ListenJobsServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method ListenJobs not implemented")
|
||||
}
|
||||
func (UnimplementedSchedulerServer) mustEmbedUnimplementedSchedulerServer() {}
|
||||
|
||||
// UnsafeSchedulerServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to SchedulerServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeSchedulerServer interface {
|
||||
mustEmbedUnimplementedSchedulerServer()
|
||||
}
|
||||
|
||||
func RegisterSchedulerServer(s grpc.ServiceRegistrar, srv SchedulerServer) {
|
||||
s.RegisterService(&Scheduler_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Scheduler_RunJob_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RunJobRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(SchedulerServer).RunJob(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/gocron_server.Scheduler/RunJob",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(SchedulerServer).RunJob(ctx, req.(*RunJobRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Scheduler_ListenJobs_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(ListenJobRequest)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(SchedulerServer).ListenJobs(m, &schedulerListenJobsServer{stream})
|
||||
}
|
||||
|
||||
type Scheduler_ListenJobsServer interface {
|
||||
Send(*ListenJobResponse) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type schedulerListenJobsServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *schedulerListenJobsServer) Send(m *ListenJobResponse) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
// Scheduler_ServiceDesc is the grpc.ServiceDesc for Scheduler service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Scheduler_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "gocron_server.Scheduler",
|
||||
HandlerType: (*SchedulerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "RunJob",
|
||||
Handler: _Scheduler_RunJob_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "ListenJobs",
|
||||
Handler: _Scheduler_ListenJobs_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "proto/gocron-server.proto",
|
||||
}
|
106
pkg/scheduler_service.go
Normal file
106
pkg/scheduler_service.go
Normal file
@ -0,0 +1,106 @@
|
||||
package gocron_server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-co-op/gocron"
|
||||
pb "github.com/strnophix/gocron-server/pkg/proto"
|
||||
)
|
||||
|
||||
type JobFunc func() (string, error)
|
||||
type UnitStore map[string]*SchedulerUnit
|
||||
type JobStore map[string]*gocron.Job
|
||||
|
||||
type SchedulerService struct {
|
||||
pb.UnimplementedSchedulerServer
|
||||
UnitStore
|
||||
JobStore
|
||||
EventBroadcaster
|
||||
|
||||
Scheduler *gocron.Scheduler
|
||||
}
|
||||
|
||||
func NewSchedulerService() *SchedulerService {
|
||||
scheduler := gocron.NewScheduler(time.UTC)
|
||||
scheduler.StartAsync()
|
||||
|
||||
return &SchedulerService{
|
||||
Scheduler: scheduler,
|
||||
UnitStore: make(UnitStore),
|
||||
JobStore: make(JobStore),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SchedulerService) Shutdown() {
|
||||
s.Scheduler.Stop()
|
||||
}
|
||||
|
||||
func (s *SchedulerService) AddUnit(unit *SchedulerUnit) error {
|
||||
s.UnitStore[unit.Name] = unit
|
||||
|
||||
if unit.Cron != "" {
|
||||
routine := s.BuildRoutine(unit)
|
||||
job, err := s.Scheduler.Cron(unit.Cron).SingletonMode().Do(routine)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.JobStore[unit.Name] = job
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SchedulerService) BuildRoutine(unit *SchedulerUnit) func() {
|
||||
return func() {
|
||||
out, err := unit.Exec.Call()
|
||||
|
||||
if err != nil {
|
||||
msg := NewBroadcastResponse(unit.Name, err.Error())
|
||||
s.EventBroadcaster.Publish(msg)
|
||||
return
|
||||
}
|
||||
|
||||
msg := NewBroadcastResponse(unit.Name, out)
|
||||
s.EventBroadcaster.Publish(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func NewRunJobError(reason string) (*pb.RunJobResponse, error) {
|
||||
return &pb.RunJobResponse{}, fmt.Errorf(reason)
|
||||
}
|
||||
|
||||
func NewRunJobSucces() (*pb.RunJobResponse, error) {
|
||||
return &pb.RunJobResponse{}, nil
|
||||
}
|
||||
|
||||
func (s *SchedulerService) RunJob(ctx context.Context, req *pb.RunJobRequest) (*pb.RunJobResponse, error) {
|
||||
unit, exists := s.UnitStore[req.UnitName]
|
||||
if !exists {
|
||||
return NewRunJobError(fmt.Sprintf("Unit with name %s does not exist", req.UnitName))
|
||||
}
|
||||
|
||||
routine := s.BuildRoutine(unit)
|
||||
|
||||
if req.RunAt != 0 {
|
||||
ts := time.Unix(req.RunAt, 0).UTC()
|
||||
job, err := s.Scheduler.Every(1).Day().At(ts).LimitRunsTo(1).SingletonMode().Do(routine)
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Unix run error: %v", err)
|
||||
}
|
||||
|
||||
s.JobStore[unit.Name] = job
|
||||
return NewRunJobSucces()
|
||||
}
|
||||
|
||||
go routine()
|
||||
|
||||
return NewRunJobSucces()
|
||||
}
|
||||
|
||||
func (s *SchedulerService) ListenJobs(req *pb.ListenJobRequest, stream pb.Scheduler_ListenJobsServer) error {
|
||||
s.EventBroadcaster.Subscribe(stream)
|
||||
<-stream.Context().Done()
|
||||
return nil
|
||||
}
|
71
pkg/scheduler_service_test.go
Normal file
71
pkg/scheduler_service_test.go
Normal file
@ -0,0 +1,71 @@
|
||||
package gocron_server_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gocron_server "github.com/strnophix/gocron-server/pkg"
|
||||
pb "github.com/strnophix/gocron-server/pkg/proto"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/test/bufconn"
|
||||
)
|
||||
|
||||
const bufSize = 1024 * 1024
|
||||
|
||||
var lis *bufconn.Listener
|
||||
var c *Counter
|
||||
|
||||
func init() {
|
||||
c = &Counter{Current: 1}
|
||||
|
||||
lis = bufconn.Listen(bufSize)
|
||||
gs := grpc.NewServer()
|
||||
s := gocron_server.NewSchedulerService()
|
||||
defer s.Shutdown()
|
||||
|
||||
incr := gocron_server.NewUnitExecFn(c.Increment)
|
||||
unit := gocron_server.NewManualUnit("incr", incr)
|
||||
s.AddUnit(unit)
|
||||
|
||||
pb.RegisterSchedulerServer(gs, s)
|
||||
go func() {
|
||||
if err := gs.Serve(lis); err != nil {
|
||||
log.Fatalf("Failed to serve: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func bufDialer(context.Context, string) (net.Conn, error) {
|
||||
return lis.Dial()
|
||||
}
|
||||
|
||||
func TestRunJob(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
conn, err := grpc.DialContext(ctx, "bufnet", grpc.WithContextDialer(bufDialer), grpc.WithInsecure())
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to dial bufnet: %v", err)
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
client := pb.NewSchedulerClient(conn)
|
||||
|
||||
_, err = client.RunJob(ctx, &pb.RunJobRequest{UnitName: "niku"})
|
||||
if err == nil {
|
||||
t.Fatalf("RunJob should have returned an error for unit niku")
|
||||
}
|
||||
|
||||
_, err = client.RunJob(ctx, &pb.RunJobRequest{UnitName: "incr"})
|
||||
if err != nil {
|
||||
t.Fatalf("RunJob call should have passed but got: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
if c.Current != 2 {
|
||||
t.Fatalf("RunJob call `incr` should have incremented counter but stays at: %d", c.Current)
|
||||
}
|
||||
}
|
66
pkg/scheduler_unit.go
Normal file
66
pkg/scheduler_unit.go
Normal file
@ -0,0 +1,66 @@
|
||||
package gocron_server
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UnitExecutable interface {
|
||||
Call() (string, error)
|
||||
}
|
||||
|
||||
type UnitExecCmd struct {
|
||||
name string
|
||||
args []string
|
||||
}
|
||||
|
||||
func (ue *UnitExecCmd) Call() (string, error) {
|
||||
cmd := exec.Command(ue.name, ue.args...)
|
||||
out, err := cmd.Output()
|
||||
strout := string(out[:])
|
||||
|
||||
if err != nil {
|
||||
return strout, err
|
||||
}
|
||||
|
||||
return strout, nil
|
||||
}
|
||||
|
||||
func NewUnitExecCmd(command string) *UnitExecCmd {
|
||||
cmdFrags := strings.Split(command, " ")
|
||||
return &UnitExecCmd{name: cmdFrags[0], args: cmdFrags[1:]}
|
||||
}
|
||||
|
||||
type UnitExecFn struct {
|
||||
fn JobFunc
|
||||
}
|
||||
|
||||
func (ue *UnitExecFn) Call() (string, error) {
|
||||
return ue.fn()
|
||||
}
|
||||
|
||||
func NewUnitExecFn(fn JobFunc) *UnitExecFn {
|
||||
return &UnitExecFn{fn}
|
||||
}
|
||||
|
||||
type SchedulerUnit struct {
|
||||
Name string
|
||||
Exec UnitExecutable
|
||||
Cron string
|
||||
}
|
||||
|
||||
func NewSchedulerUnit(name, cron string, exec UnitExecutable) *SchedulerUnit {
|
||||
return &SchedulerUnit{
|
||||
Name: name,
|
||||
Exec: exec,
|
||||
Cron: cron,
|
||||
}
|
||||
}
|
||||
|
||||
func NewManualUnit(name string, exec UnitExecutable) *SchedulerUnit {
|
||||
return &SchedulerUnit{
|
||||
Name: name,
|
||||
Exec: exec,
|
||||
Cron: "",
|
||||
}
|
||||
}
|
32
pkg/scheduler_unit_test.go
Normal file
32
pkg/scheduler_unit_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package gocron_server_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
gocron_server "github.com/strnophix/gocron-server/pkg"
|
||||
)
|
||||
|
||||
func TestUnitExecutableCommand(t *testing.T) {
|
||||
cmd := gocron_server.NewUnitExecCmd("echo hi")
|
||||
out, err := cmd.Call()
|
||||
if err != nil {
|
||||
t.Fatalf("Should not have errored on a simple echo")
|
||||
}
|
||||
|
||||
if out != "hi\n" {
|
||||
t.Fatalf("Execution should have returned \"hi\" but gave: %s", out)
|
||||
}
|
||||
|
||||
c := Counter{Current: 1}
|
||||
fn := gocron_server.NewUnitExecFn(c.Increment)
|
||||
out, _ = fn.Call()
|
||||
if out != "2" {
|
||||
t.Fatalf("Execution of unit should have incremented counter but got: %d", c.Current)
|
||||
}
|
||||
|
||||
cmd = gocron_server.NewUnitExecCmd("false")
|
||||
_, err = cmd.Call()
|
||||
if err == nil {
|
||||
t.Fatalf("Should have returned an error on a non-zero exit")
|
||||
}
|
||||
}
|
12
pkg/setup_test.go
Normal file
12
pkg/setup_test.go
Normal file
@ -0,0 +1,12 @@
|
||||
package gocron_server_test
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Counter struct {
|
||||
Current int
|
||||
}
|
||||
|
||||
func (c *Counter) Increment() (string, error) {
|
||||
c.Current += 1
|
||||
return fmt.Sprint(c.Current), nil
|
||||
}
|
2
scripts/listen_jobs.sh
Executable file
2
scripts/listen_jobs.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
grpcurl --plaintext -d "{}" localhost:9092 gocron_server.Scheduler/ListenJobs
|
3
scripts/send_delayed_increment.sh
Executable file
3
scripts/send_delayed_increment.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
NEW_EPOCH=$(($(date +%s) + 5))
|
||||
grpcurl --plaintext -d "{\"UnitName\": \"incr\", \"RunAt\": $NEW_EPOCH}" localhost:9092 gocron_server.Scheduler/RunJob | jq
|
2
scripts/send_notify.sh
Executable file
2
scripts/send_notify.sh
Executable file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env bash
|
||||
grpcurl --plaintext -d "{\"UnitName\": \"$1\"}" localhost:9092 gocron_server.Scheduler/RunJob | jq
|
Loading…
x
Reference in New Issue
Block a user