├── Procfile ├── _config.yml ├── cpu.pdf ├── mem.pdf ├── test.db ├── version └── version.go ├── extra ├── gonec.png ├── logo.png ├── TODOApp.png ├── perf1c.gif ├── button_doc.png ├── button_ppt.png ├── gonec_head.png ├── langarch.png ├── langarch.vsdx ├── architecture.png ├── button_blog.png ├── button_chat.png ├── button_down.png ├── button_play.png ├── gonec_head_w.png ├── architecture.vsdx └── http_perfomance.png ├── test ├── test.gnx ├── pi.gnc ├── test.gnc ├── tcptest.gnc ├── http2.gnc └── httptest.gnc ├── ast ├── doc.go └── token.go ├── parser ├── Makefile └── optimize.go ├── PITCHME.yaml ├── .travis.yml ├── consulapi ├── operator.go ├── operator_segment.go ├── raw.go ├── README.md ├── status.go ├── snapshot.go ├── coordinate.go ├── operator_keyring.go ├── operator_raft.go ├── event.go ├── acl.go ├── catalog.go ├── health.go ├── operator_area.go ├── session.go ├── prepared_query.go └── operator_autopilot.go ├── PITCHME.css ├── .gitignore ├── .vscode └── launch.json ├── TODO ├── pos └── pos.go ├── bincode ├── binfuncs.go ├── binstmt │ └── binvmtypes.go └── binregs.go ├── go.mod ├── Gopkg.toml ├── core ├── corefunc.go ├── corechan.go ├── corewaitgroup.go ├── corecrypt_test.go ├── coretable.go ├── coreclient.go ├── corecrypt.go ├── coreerrors.go ├── coreifaces.go ├── coreservice.go ├── corebool.go ├── coremetaobj.go ├── coreserver.go ├── corestring.go └── coreint.go ├── LICENSE ├── README.md ├── names └── uniquenames.go ├── tool └── makebuiltin.go ├── services └── gonecsvc │ ├── jsAceTheme.go │ ├── jsAceLang.go │ └── gonecsvc.go ├── Gopkg.lock ├── main_test.go ├── example └── todo.gnc ├── PITCHME.md └── main.go /Procfile: -------------------------------------------------------------------------------- 1 | web: gonec -t -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-tactile -------------------------------------------------------------------------------- /cpu.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/cpu.pdf -------------------------------------------------------------------------------- /mem.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/mem.pdf -------------------------------------------------------------------------------- /test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/test.db -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | const Version = "3.6" 4 | -------------------------------------------------------------------------------- /extra/gonec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/gonec.png -------------------------------------------------------------------------------- /extra/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/logo.png -------------------------------------------------------------------------------- /test/test.gnx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/test/test.gnx -------------------------------------------------------------------------------- /extra/TODOApp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/TODOApp.png -------------------------------------------------------------------------------- /extra/perf1c.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/perf1c.gif -------------------------------------------------------------------------------- /ast/doc.go: -------------------------------------------------------------------------------- 1 | // Package ast implements abstruct-syntax-tree for gonec. 2 | package ast 3 | -------------------------------------------------------------------------------- /extra/button_doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/button_doc.png -------------------------------------------------------------------------------- /extra/button_ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/button_ppt.png -------------------------------------------------------------------------------- /extra/gonec_head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/gonec_head.png -------------------------------------------------------------------------------- /extra/langarch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/langarch.png -------------------------------------------------------------------------------- /extra/langarch.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/langarch.vsdx -------------------------------------------------------------------------------- /extra/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/architecture.png -------------------------------------------------------------------------------- /extra/button_blog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/button_blog.png -------------------------------------------------------------------------------- /extra/button_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/button_chat.png -------------------------------------------------------------------------------- /extra/button_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/button_down.png -------------------------------------------------------------------------------- /extra/button_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/button_play.png -------------------------------------------------------------------------------- /extra/gonec_head_w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/gonec_head_w.png -------------------------------------------------------------------------------- /extra/architecture.vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/architecture.vsdx -------------------------------------------------------------------------------- /extra/http_perfomance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/covrom/gonec/HEAD/extra/http_perfomance.png -------------------------------------------------------------------------------- /parser/Makefile: -------------------------------------------------------------------------------- 1 | all : parser.go 2 | 3 | parser.go : parser.go.y 4 | goyacc -o $@ parser.go.y 5 | -------------------------------------------------------------------------------- /PITCHME.yaml: -------------------------------------------------------------------------------- 1 | logo : extra/gonec_head.png 2 | theme-override : PITCHME.css 3 | autoslide : 5000 4 | transition : fade -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.11.2 4 | before_install: 5 | - go get github.com/daviddengcn/go-colortext 6 | - go get github.com/mattn/go-isatty -------------------------------------------------------------------------------- /test/pi.gnc: -------------------------------------------------------------------------------- 1 | N = 10000000 2 | sum = 0.0 3 | x = 0.0 4 | dx = 1.0 / Число(N) 5 | Для н = 0 по N-1 Цикл 6 | sum += 4.0 / (1.0 + x * x) 7 | x += dx 8 | КонецЦикла 9 | pi = dx * sum 10 | Сообщить(pi) -------------------------------------------------------------------------------- /ast/token.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import "github.com/covrom/gonec/pos" 4 | 5 | type Token struct { 6 | pos.PosImpl // StmtImpl provide Pos() function. 7 | Tok int 8 | Lit string 9 | } 10 | -------------------------------------------------------------------------------- /consulapi/operator.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | // Operator can be used to perform low-level operator tasks for Consul. 4 | type Operator struct { 5 | c *Client 6 | } 7 | 8 | // Operator returns a handle to the operator endpoints. 9 | func (c *Client) Operator() *Operator { 10 | return &Operator{c} 11 | } 12 | -------------------------------------------------------------------------------- /test/test.gnc: -------------------------------------------------------------------------------- 1 | сообщить("Замер производительности") 2 | дтнач = ТекущаяДата() 3 | 4 | функция фиб(н) 5 | если н = 0 тогда 6 | возврат 0 7 | иначеесли н = 1 тогда 8 | возврат 1 9 | конецесли 10 | возврат фиб(н-1) + фиб(н-2) 11 | конецфункции 12 | 13 | к=фиб(35) 14 | сообщить(ПрошлоВремениС(дтнач)) 15 | сообщить(к) -------------------------------------------------------------------------------- /PITCHME.css: -------------------------------------------------------------------------------- 1 | .reveal { 2 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; 3 | font-size: 26px; 4 | font-weight: normal; 5 | text-align: left; 6 | } 7 | 8 | .reveal section img { 9 | border: 0; 10 | box-shadow: none; } -------------------------------------------------------------------------------- /consulapi/operator_segment.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | // SegmentList returns all the available LAN segments. 4 | func (op *Operator) SegmentList(q *QueryOptions) ([]string, *QueryMeta, error) { 5 | var out []string 6 | qm, err := op.c.query("/v1/operator/segment", &out, q) 7 | if err != nil { 8 | return nil, nil, err 9 | } 10 | return out, qm, nil 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | 13 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 14 | .glide/ 15 | 16 | debug 17 | gonec 18 | y.output 19 | 20 | test.db 21 | todo.db 22 | todo.db.lock 23 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "go", 7 | "request": "launch", 8 | "mode": "debug", 9 | "remotePath": "", 10 | "port": 2345, 11 | "host": "127.0.0.1", 12 | "program": "${fileDirname}", 13 | "env": {}, 14 | "args": [], 15 | "showLog": true 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /parser/optimize.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/covrom/gonec/ast" 5 | ) 6 | 7 | func ConstFolding(inast ast.Stmts) ast.Stmts { 8 | 9 | // num := 4 10 | // ch := make(chan ast.Stmt, 20) 11 | // done := make(chan bool, 20) 12 | // ast.StartStmtSimplifyWorkers(ch, done, num) 13 | 14 | for i := range inast { 15 | inast[i].Simplify() 16 | // ch <- inast[i] 17 | } 18 | // for i := 0; i < num; i++ { 19 | // done <- true 20 | // } 21 | 22 | return inast 23 | } 24 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Неверная работа с полями структур как идентификаторами 2 | 3 | микросервис хранения и выдачи настроек 4 | 5 | распараллеливание циклов по директиве ПАРАЛЛЕЛЬНО 6 | 7 | ДвоичныеДанные, io 8 | 9 | HTTPS 10 | 11 | RegExp 12 | 13 | Мьютекс 14 | 15 | Замыкания из go-lua 16 | 17 | consul service discovery - хранение и получение настроек 18 | 19 | Kubernetes service discovery 20 | 21 | Микросервис авторизации 22 | 23 | Таблицы значений как метатаблицы из Lua, на основе sync.Map, с расширенными матричными функциями и встраиваемыми обработчиками 24 | 25 | influxdb 26 | dgraph 27 | 28 | Telegram-бот 29 | 30 | Система рекомендаций на базе dgraph 31 | 32 | Тестировать и окончательно документировать все конструкции языка 33 | -------------------------------------------------------------------------------- /pos/pos.go: -------------------------------------------------------------------------------- 1 | package pos 2 | 3 | // Position provides interface to store code locations. 4 | type Position struct { 5 | Line int 6 | Column int 7 | } 8 | 9 | // Pos interface provies two functions to get/set the position for expression or statement. 10 | type Pos interface { 11 | Position() Position 12 | SetPosition(Position) 13 | } 14 | 15 | // PosImpl provies commonly implementations for Pos. 16 | type PosImpl struct { 17 | Pos Position 18 | } 19 | 20 | // Position return the position of the expression or statement. 21 | func (x *PosImpl) Position() Position { 22 | return x.Pos 23 | } 24 | 25 | // SetPosition is a function to specify position of the expression or statement. 26 | func (x *PosImpl) SetPosition(pos Position) { 27 | x.Pos = pos 28 | } 29 | -------------------------------------------------------------------------------- /bincode/binfuncs.go: -------------------------------------------------------------------------------- 1 | package bincode 2 | 3 | func LeftRightBounds(rb, re int, vlen int) (ii, ij int) { 4 | // границы как в python: 5 | // положительный - имеет максимум до длины (len) 6 | // отрицательный - считается с конца с минимумом -длина 7 | // если выходит за макс. границу - возвращаем пустой слайс 8 | // если выходит за мин. границу - считаем =0 9 | 10 | // правая граница как в python - исключается 11 | 12 | // левая граница включая 13 | ii = rb 14 | 15 | switch { 16 | case ii > 0: 17 | if ii >= vlen { 18 | ii = vlen - 1 19 | } 20 | case ii < 0: 21 | ii += vlen 22 | if ii < 0 { 23 | ii = 0 24 | } 25 | } 26 | 27 | ij = re 28 | 29 | switch { 30 | case ij > 0: 31 | if ij > vlen { 32 | ij = vlen 33 | } 34 | case ij < 0: 35 | ij += vlen 36 | if ij < 0 { 37 | ij = 0 38 | } 39 | } 40 | return 41 | } 42 | 43 | -------------------------------------------------------------------------------- /consulapi/raw.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | // Raw can be used to do raw queries against custom endpoints 4 | type Raw struct { 5 | c *Client 6 | } 7 | 8 | // Raw returns a handle to query endpoints 9 | func (c *Client) Raw() *Raw { 10 | return &Raw{c} 11 | } 12 | 13 | // Query is used to do a GET request against an endpoint 14 | // and deserialize the response into an interface using 15 | // standard Consul conventions. 16 | func (raw *Raw) Query(endpoint string, out interface{}, q *QueryOptions) (*QueryMeta, error) { 17 | return raw.c.query(endpoint, out, q) 18 | } 19 | 20 | // Write is used to do a PUT request against an endpoint 21 | // and serialize/deserialized using the standard Consul conventions. 22 | func (raw *Raw) Write(endpoint string, in, out interface{}, q *WriteOptions) (*WriteMeta, error) { 23 | return raw.c.write(endpoint, in, out, q) 24 | } 25 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/covrom/gonec 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/boltdb/bolt v1.3.1 7 | github.com/covrom/decnum v0.0.0-20181120130125-279518e708a6 8 | github.com/daviddengcn/go-colortext v1.0.0 9 | github.com/dchest/siphash v1.2.3 10 | github.com/hashicorp/go-cleanhttp v0.5.2 11 | github.com/hashicorp/go-rootcerts v1.0.2 12 | github.com/hashicorp/serf v0.10.1 13 | github.com/mattn/go-isatty v0.0.16 14 | github.com/satori/go.uuid v1.2.0 15 | ) 16 | 17 | require ( 18 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect 19 | github.com/hashicorp/go-immutable-radix v1.0.0 // indirect 20 | github.com/hashicorp/golang-lru v0.5.0 // indirect 21 | github.com/mitchellh/go-homedir v1.1.0 // indirect 22 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect 23 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect 24 | ) 25 | -------------------------------------------------------------------------------- /consulapi/README.md: -------------------------------------------------------------------------------- 1 | Consul API client 2 | ================= 3 | 4 | This package provides the `api` package which attempts to 5 | provide programmatic access to the full Consul API. 6 | 7 | Currently, all of the Consul APIs included in version 0.6.0 are supported. 8 | 9 | Documentation 10 | ============= 11 | 12 | The full documentation is available on [Godoc](https://godoc.org/github.com/hashicorp/consul/api) 13 | 14 | Usage 15 | ===== 16 | 17 | Below is an example of using the Consul client: 18 | 19 | ```go 20 | // Get a new client 21 | client, err := api.NewClient(api.DefaultConfig()) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | // Get a handle to the KV API 27 | kv := client.KV() 28 | 29 | // PUT a new KV pair 30 | p := &api.KVPair{Key: "foo", Value: []byte("test")} 31 | _, err = kv.Put(p, nil) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | // Lookup the pair 37 | pair, _, err := kv.Get("foo", nil) 38 | if err != nil { 39 | panic(err) 40 | } 41 | fmt.Printf("KV: %v", pair) 42 | 43 | ``` 44 | -------------------------------------------------------------------------------- /consulapi/status.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | // Status can be used to query the Status endpoints 4 | type Status struct { 5 | c *Client 6 | } 7 | 8 | // Status returns a handle to the status endpoints 9 | func (c *Client) Status() *Status { 10 | return &Status{c} 11 | } 12 | 13 | // Leader is used to query for a known leader 14 | func (s *Status) Leader() (string, error) { 15 | r := s.c.newRequest("GET", "/v1/status/leader") 16 | _, resp, err := requireOK(s.c.doRequest(r)) 17 | if err != nil { 18 | return "", err 19 | } 20 | defer resp.Body.Close() 21 | 22 | var leader string 23 | if err := decodeBody(resp, &leader); err != nil { 24 | return "", err 25 | } 26 | return leader, nil 27 | } 28 | 29 | // Peers is used to query for a known raft peers 30 | func (s *Status) Peers() ([]string, error) { 31 | r := s.c.newRequest("GET", "/v1/status/peers") 32 | _, resp, err := requireOK(s.c.doRequest(r)) 33 | if err != nil { 34 | return nil, err 35 | } 36 | defer resp.Body.Close() 37 | 38 | var peers []string 39 | if err := decodeBody(resp, &peers); err != nil { 40 | return nil, err 41 | } 42 | return peers, nil 43 | } 44 | -------------------------------------------------------------------------------- /test/tcptest.gnc: -------------------------------------------------------------------------------- 1 | дтнач = ТекущаяДата() 2 | 3 | Функция ОбработатьСерв(соед) 4 | Сообщить("Сервер получил соединение:",соед) 5 | Сообщить("Получен запрос:",соед.Получить()) 6 | КонецФункции 7 | 8 | серв = Новый Сервер 9 | Попытка 10 | серв.Открыть("tcp", "127.0.0.1:9990", 1000, ОбработатьСерв, 0) 11 | Исключение 12 | Сообщить(ОписаниеОшибки()) 13 | Сообщить("Кажется сервер уже запущен, или тут какая-то другая ошибка, но мы все равно попробуем отправить запрос :)") 14 | КонецПопытки 15 | 16 | клиенты = [] 17 | гр = Новый ГруппаОжидания 18 | 19 | фобр = Функция (соед) 20 | д = соед.Данные() 21 | Сообщить("Устанавливаем соединение:",соед) 22 | запр={ 23 | "id":соед.Идентификатор(), 24 | "query":"Запрос по tcp протоколу", 25 | "num":д[1], 26 | } 27 | Сообщить("Отправляем:", запр) 28 | соед.Отправить(запр) 29 | д[0].Завершить() 30 | КонецФункции 31 | 32 | Для н=1 по 1000 Цикл 33 | кли = Новый Клиент 34 | гр.Добавить(1) 35 | кли.Открыть("tcp", "127.0.0.1:9990", фобр, [гр, н]) 36 | клиенты += кли 37 | КонецЦикла 38 | 39 | гр.Ожидать() 40 | серв.Закрыть() 41 | 42 | Сообщить("Все завершилось просто идеально за", ПрошлоВремениС(дтнач), "!") 43 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | name = "github.com/dchest/siphash" 30 | version = "1.1.0" 31 | 32 | [[constraint]] 33 | branch = "master" 34 | name = "github.com/hashicorp/go-rootcerts" 35 | 36 | [[constraint]] 37 | name = "github.com/mattn/go-isatty" 38 | version = "0.0.2" 39 | 40 | [prune] 41 | go-tests = true 42 | unused-packages = true 43 | [metadata.heroku] 44 | root-package = "github.com/covrom/gonec" 45 | go-version = "1.11.2" 46 | # install = [ "./..." ] -------------------------------------------------------------------------------- /test/http2.gnc: -------------------------------------------------------------------------------- 1 | Функция бенч2() 2 | дтнач = ТекущаяДата() 3 | 4 | ОбработатьHTTP = Функция (вых,вх) 5 | Сообщить("Сервер получил запрос:\n",вх.Сообщение()) 6 | вых.Отправить({"Статус":200, "Тело":"Запрос обработан успешно"}) 7 | КонецФункции 8 | 9 | серв = Новый Сервер 10 | Попытка 11 | серв.Открыть("http", "127.0.0.1:9990", 1000, {"/test":ОбработатьHTTP}, 0) 12 | Исключение 13 | Сообщить(ОписаниеОшибки()) 14 | Сообщить("Кажется сервер уже запущен, или тут какая-то другая ошибка, но мы все равно попробуем отправить запрос :)") 15 | КонецПопытки 16 | 17 | Для н=1 по 1000 Цикл 18 | кли = Новый Клиент 19 | соед = кли.Соединить("http","") 20 | Сообщить("Устанавливаем соединение:",соед) 21 | запр={ 22 | "Метод": "GET", 23 | "Путь": "http://127.0.0.1:9990/test", 24 | "Заголовки": { 25 | "ReqId":соед.Идентификатор(), 26 | }, 27 | "Параметры": { 28 | "id":соед.Идентификатор(), 29 | "query":"Запрос по http протоколу", 30 | "num": н, 31 | }, 32 | } 33 | Сообщить("Отправляем:", запр) 34 | Попытка 35 | Сообщить("Ответ",н,":",соед.Запрос(запр)) 36 | Исключение 37 | Сообщить(ОписаниеОшибки()) 38 | КонецПопытки 39 | кли.Закрыть() 40 | КонецЦикла 41 | 42 | серв.Закрыть() 43 | 44 | Возврат ПрошлоВремениС(дтнач) 45 | КонецФункции 46 | 47 | Сообщить("Все завершилось просто идеально: HTTP за", бенч2(), "!") 48 | 49 | -------------------------------------------------------------------------------- /core/corefunc.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // VMFunc вызывается как обертка метода объекта метаданных или обертка функции библиотеки 8 | // возвращаемое из обертки значение должно быть приведено к типу вирт. машины 9 | // функции такого типа создаются на языке Гонец, 10 | // их можно использовать в стандартной библиотеке, проверив на этот тип 11 | // в args передаются входные параметры, в rets передается ссылка на слайс возвращаемых значений - он заполняется в функции 12 | // при возврате так же возвращается окружение в envout, в котором выполнялась функция 13 | // это нужно для обработки callback-вызова из Го, например, отправки ее сообщения об ошибке в ее же окружение 14 | type VMFunc func(args VMSlice, rets *VMSlice, envout *(*Env)) error 15 | 16 | func (f VMFunc) vmval() {} 17 | 18 | func (f VMFunc) Interface() interface{} { 19 | return f 20 | } 21 | 22 | func (f VMFunc) String() string { 23 | return fmt.Sprintf("[Функция: %p]", f) 24 | } 25 | 26 | func (f VMFunc) Func() VMFunc { 27 | return f 28 | } 29 | 30 | type VMMethod = func(VMSlice, *VMSlice, *(*Env)) error 31 | 32 | func VMFuncMustParams(n int, f VMMethod) VMFunc { 33 | return VMFunc( 34 | func(args VMSlice, rets *VMSlice, envout *(*Env)) error { 35 | if len(args) != n { 36 | switch n { 37 | case 0: 38 | return VMErrorNoNeedArgs 39 | default: 40 | return VMErrorNeedArgs(n) 41 | } 42 | } 43 | return f(args, rets, envout) 44 | }) 45 | } 46 | -------------------------------------------------------------------------------- /consulapi/snapshot.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | import ( 4 | "io" 5 | ) 6 | 7 | // Snapshot can be used to query the /v1/snapshot endpoint to take snapshots of 8 | // Consul's internal state and restore snapshots for disaster recovery. 9 | type Snapshot struct { 10 | c *Client 11 | } 12 | 13 | // Snapshot returns a handle that exposes the snapshot endpoints. 14 | func (c *Client) Snapshot() *Snapshot { 15 | return &Snapshot{c} 16 | } 17 | 18 | // Save requests a new snapshot and provides an io.ReadCloser with the snapshot 19 | // data to save. If this doesn't return an error, then it's the responsibility 20 | // of the caller to close it. Only a subset of the QueryOptions are supported: 21 | // Datacenter, AllowStale, and Token. 22 | func (s *Snapshot) Save(q *QueryOptions) (io.ReadCloser, *QueryMeta, error) { 23 | r := s.c.newRequest("GET", "/v1/snapshot") 24 | r.setQueryOptions(q) 25 | 26 | rtt, resp, err := requireOK(s.c.doRequest(r)) 27 | if err != nil { 28 | return nil, nil, err 29 | } 30 | 31 | qm := &QueryMeta{} 32 | parseQueryMeta(resp, qm) 33 | qm.RequestTime = rtt 34 | return resp.Body, qm, nil 35 | } 36 | 37 | // Restore streams in an existing snapshot and attempts to restore it. 38 | func (s *Snapshot) Restore(q *WriteOptions, in io.Reader) error { 39 | r := s.c.newRequest("PUT", "/v1/snapshot") 40 | r.body = in 41 | r.setWriteOptions(q) 42 | _, _, err := requireOK(s.c.doRequest(r)) 43 | if err != nil { 44 | return err 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /core/corechan.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "github.com/covrom/gonec/names" 5 | ) 6 | 7 | // VMChan - канал для передачи любого типа вирт. машины 8 | type VMChan chan VMValuer 9 | 10 | func (x VMChan) vmval() {} 11 | 12 | func (x VMChan) Interface() interface{} { 13 | return x 14 | } 15 | 16 | func (x VMChan) Send(v VMValuer) { 17 | x <- v 18 | } 19 | 20 | func (x VMChan) Recv() (VMValuer, bool) { 21 | rv, ok := <-x 22 | return rv, ok 23 | } 24 | 25 | func (x VMChan) TrySend(v VMValuer) (ok bool) { 26 | select { 27 | case x <- v: 28 | ok = true 29 | default: 30 | ok = false 31 | } 32 | return 33 | } 34 | 35 | func (x VMChan) TryRecv() (v VMValuer, ok bool, notready bool) { 36 | select { 37 | case v, ok = <-x: 38 | notready = false 39 | default: 40 | ok = false 41 | notready = true 42 | } 43 | return 44 | } 45 | 46 | func (x VMChan) Close() { close(x) } 47 | 48 | func (x VMChan) Size() int { return cap(x) } 49 | 50 | func (x VMChan) MethodMember(name int) (VMFunc, bool) { 51 | 52 | // только эти методы будут доступны из кода на языке Гонец! 53 | switch names.UniqueNames.GetLowerCase(name) { 54 | case "закрыть": 55 | return VMFuncMustParams(0, x.Закрыть), true 56 | case "размер": 57 | return VMFuncMustParams(0, x.Размер), true 58 | // TODO: подключить соединение 59 | } 60 | return nil, false 61 | } 62 | 63 | func (x VMChan) Закрыть(args VMSlice, rets *VMSlice, envout *(*Env)) error { 64 | x.Close() 65 | return nil 66 | } 67 | 68 | func (x VMChan) Размер(args VMSlice, rets *VMSlice, envout *(*Env)) error { 69 | rets.Append(VMInt(x.Size())) 70 | return nil 71 | } 72 | -------------------------------------------------------------------------------- /core/corewaitgroup.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | 7 | "github.com/covrom/gonec/names" 8 | ) 9 | 10 | // VMWaitGroup - группа ожидания исполнения горутин 11 | type VMWaitGroup struct { 12 | wg sync.WaitGroup 13 | } 14 | 15 | var ReflectVMWaitGroup = reflect.TypeOf(VMWaitGroup{}) 16 | 17 | func (x *VMWaitGroup) vmval() {} 18 | 19 | func (x *VMWaitGroup) Interface() interface{} { 20 | return x 21 | } 22 | 23 | func (x *VMWaitGroup) String() string { 24 | return "Группа ожидания" 25 | } 26 | 27 | func (x *VMWaitGroup) Add(delta int) { 28 | x.wg.Add(delta) 29 | } 30 | 31 | func (x *VMWaitGroup) Done() { 32 | x.wg.Done() 33 | } 34 | 35 | func (x *VMWaitGroup) Wait() { 36 | x.wg.Wait() 37 | } 38 | 39 | func (x *VMWaitGroup) MethodMember(name int) (VMFunc, bool) { 40 | 41 | // только эти методы будут доступны из кода на языке Гонец! 42 | switch names.UniqueNames.GetLowerCase(name) { 43 | case "добавить": 44 | return VMFuncMustParams(1, x.Добавить), true 45 | case "завершить": 46 | return VMFuncMustParams(0, x.Завершить), true 47 | case "ожидать": 48 | return VMFuncMustParams(0, x.Ожидать), true 49 | } 50 | return nil, false 51 | } 52 | 53 | func (x *VMWaitGroup) Добавить(args VMSlice, rets *VMSlice, envout *(*Env)) error { 54 | v, ok := args[0].(VMInt) 55 | if !ok { 56 | return VMErrorNeedInt 57 | } 58 | x.Add(int(v)) 59 | return nil 60 | } 61 | 62 | func (x *VMWaitGroup) Завершить(args VMSlice, rets *VMSlice, envout *(*Env)) error { 63 | x.Done() 64 | return nil 65 | } 66 | 67 | func (x *VMWaitGroup) Ожидать(args VMSlice, rets *VMSlice, envout *(*Env)) error { 68 | x.Wait() 69 | return nil 70 | } 71 | -------------------------------------------------------------------------------- /consulapi/coordinate.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | import ( 4 | "github.com/hashicorp/serf/coordinate" 5 | ) 6 | 7 | // CoordinateEntry represents a node and its associated network coordinate. 8 | type CoordinateEntry struct { 9 | Node string 10 | Segment string 11 | Coord *coordinate.Coordinate 12 | } 13 | 14 | // CoordinateDatacenterMap has the coordinates for servers in a given datacenter 15 | // and area. Network coordinates are only compatible within the same area. 16 | type CoordinateDatacenterMap struct { 17 | Datacenter string 18 | AreaID string 19 | Coordinates []CoordinateEntry 20 | } 21 | 22 | // Coordinate can be used to query the coordinate endpoints 23 | type Coordinate struct { 24 | c *Client 25 | } 26 | 27 | // Coordinate returns a handle to the coordinate endpoints 28 | func (c *Client) Coordinate() *Coordinate { 29 | return &Coordinate{c} 30 | } 31 | 32 | // Datacenters is used to return the coordinates of all the servers in the WAN 33 | // pool. 34 | func (c *Coordinate) Datacenters() ([]*CoordinateDatacenterMap, error) { 35 | r := c.c.newRequest("GET", "/v1/coordinate/datacenters") 36 | _, resp, err := requireOK(c.c.doRequest(r)) 37 | if err != nil { 38 | return nil, err 39 | } 40 | defer resp.Body.Close() 41 | 42 | var out []*CoordinateDatacenterMap 43 | if err := decodeBody(resp, &out); err != nil { 44 | return nil, err 45 | } 46 | return out, nil 47 | } 48 | 49 | // Nodes is used to return the coordinates of all the nodes in the LAN pool. 50 | func (c *Coordinate) Nodes(q *QueryOptions) ([]*CoordinateEntry, *QueryMeta, error) { 51 | r := c.c.newRequest("GET", "/v1/coordinate/nodes") 52 | r.setQueryOptions(q) 53 | rtt, resp, err := requireOK(c.c.doRequest(r)) 54 | if err != nil { 55 | return nil, nil, err 56 | } 57 | defer resp.Body.Close() 58 | 59 | qm := &QueryMeta{} 60 | parseQueryMeta(resp, qm) 61 | qm.RequestTime = rtt 62 | 63 | var out []*CoordinateEntry 64 | if err := decodeBody(resp, &out); err != nil { 65 | return nil, nil, err 66 | } 67 | return out, qm, nil 68 | } 69 | -------------------------------------------------------------------------------- /bincode/binstmt/binvmtypes.go: -------------------------------------------------------------------------------- 1 | package binstmt 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | posit "github.com/covrom/gonec/pos" 8 | ) 9 | 10 | // Error provides a convenient interface for handling runtime error. 11 | // It can be Error interface with type cast which can call Pos(). 12 | type Error struct { 13 | Message string 14 | Pos posit.Position 15 | } 16 | 17 | var ( 18 | BreakError = errors.New("Неверное применение оператора Прервать") 19 | ContinueError = errors.New("Неверное применение оператора Продолжить") 20 | ReturnError = errors.New("Неверное применение оператора Возврат") 21 | InterruptError = errors.New("Выполнение прервано") 22 | ) 23 | 24 | // NewStringError makes error interface with message. 25 | func NewStringError(pos posit.Pos, err string) error { 26 | if pos == nil { 27 | return &Error{Message: err, Pos: posit.Position{1, 1}} 28 | } 29 | return &Error{Message: err, Pos: pos.Position()} 30 | } 31 | 32 | // NewErrorf makes error interface with message. 33 | func NewErrorf(pos posit.Pos, format string, args ...interface{}) error { 34 | return &Error{Message: fmt.Sprintf(format, args...), Pos: pos.Position()} 35 | } 36 | 37 | // NewError makes error interface with message. 38 | // This doesn't overwrite last error. 39 | func NewError(pos posit.Pos, err error) error { 40 | if err == nil { 41 | return nil 42 | } 43 | if err == BreakError || err == ContinueError || err == ReturnError { 44 | return err 45 | } 46 | // if pe, ok := err.(*parser.Error); ok { 47 | // return pe 48 | // } 49 | if ee, ok := err.(*Error); ok { 50 | return ee 51 | } 52 | return &Error{Message: err.Error(), Pos: pos.Position()} 53 | } 54 | 55 | // Error returns the error message. 56 | func (e *Error) Error() string { 57 | // учитываем вставку модуля _ по умолчанию - вычитаем 1 из номера строки 58 | return fmt.Sprintf("[%d:%d] %s", e.Pos.Line-1, e.Pos.Column, e.Message) 59 | } 60 | 61 | func (e *Error) String() string { 62 | // учитываем вставку модуля _ по умолчанию - вычитаем 1 из номера строки 63 | return e.Message 64 | } 65 | 66 | -------------------------------------------------------------------------------- /core/corecrypt_test.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestGZip(t *testing.T) { 9 | type args struct { 10 | src []byte 11 | } 12 | 13 | smpl := []byte(`{ 14 | "id":соед.Идентификатор(), 15 | "query":"Запрос по tcp протоколу", 16 | "num":1, 17 | }`) 18 | 19 | tests := []struct { 20 | name string 21 | args args 22 | want []byte 23 | wantErr bool 24 | }{ 25 | { 26 | name: "gzip 1", 27 | args: args{ 28 | src: smpl, 29 | }, 30 | want: smpl, 31 | wantErr: false, 32 | }, 33 | } 34 | for _, tt := range tests { 35 | t.Run(tt.name, func(t *testing.T) { 36 | gotz, err := GZip(tt.args.src) 37 | if (err != nil) != tt.wantErr { 38 | t.Errorf("GZip() error = %v, wantErr %v", err, tt.wantErr) 39 | return 40 | } 41 | t.Log(len(gotz), gotz) 42 | gotuz, err := UnGZip(gotz) 43 | if (err != nil) != tt.wantErr { 44 | t.Errorf("UnGZip() error = %v, wantErr %v", err, tt.wantErr) 45 | return 46 | } 47 | t.Log(len(gotuz), gotuz) 48 | if !reflect.DeepEqual(gotuz, tt.want) { 49 | t.Errorf("UnGZip(Gzip()) = %v, want %v", gotuz, tt.want) 50 | } 51 | }) 52 | } 53 | } 54 | 55 | func TestEncryptAES128(t *testing.T) { 56 | type args struct { 57 | plaintext []byte 58 | } 59 | smpl := []byte(`{ 60 | "id":соед.Идентификатор(), 61 | "query":"Запрос по tcp протоколу", 62 | "num":1, 63 | }`) 64 | 65 | tests := []struct { 66 | name string 67 | args args 68 | want []byte 69 | wantErr bool 70 | }{ 71 | { 72 | name: "gzip 1", 73 | args: args{ 74 | plaintext: smpl, 75 | }, 76 | want: smpl, 77 | wantErr: false, 78 | }, 79 | } 80 | for _, tt := range tests { 81 | t.Run(tt.name, func(t *testing.T) { 82 | got, err := EncryptAES128(tt.args.plaintext) 83 | if (err != nil) != tt.wantErr { 84 | t.Errorf("EncryptAES128() error = %v, wantErr %v", err, tt.wantErr) 85 | return 86 | } 87 | t.Log(len(got), got) 88 | got2, err := DecryptAES128(got) 89 | if !reflect.DeepEqual(got2, tt.want) { 90 | t.Errorf("DecryptAES128(EncryptAES128()) = %v, want %v", got2, tt.want) 91 | } 92 | }) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /test/httptest.gnc: -------------------------------------------------------------------------------- 1 | дтнач = ТекущаяДата() 2 | 3 | Функция ОбработатьСерв(вых,вх) 4 | Сообщить("Сервер получил запрос:\n",вх.Сообщение()) 5 | вых.Отправить({"Статус":200, "Тело":"Запрос обработан успешно"}) 6 | КонецФункции 7 | 8 | серв = Новый Сервер 9 | Попытка 10 | серв.Открыть("http", "127.0.0.1:9990", 1000, {"/test":ОбработатьСерв}, 0) 11 | Исключение 12 | Сообщить(ОписаниеОшибки()) 13 | Сообщить("Кажется сервер уже запущен, или тут какая-то другая ошибка, но мы все равно попробуем отправить запрос :)") 14 | КонецПопытки 15 | 16 | гр = Новый ГруппаОжидания 17 | 18 | фобр = Функция (соед) 19 | д = соед.Данные() 20 | Сообщить("Устанавливаем соединение:",соед) 21 | запр={ 22 | "Метод": "GET", 23 | "Путь": "http://127.0.0.1:9990/test", 24 | "Заголовки": { 25 | "ReqId":соед.Идентификатор(), 26 | }, 27 | "Параметры": { 28 | "id":соед.Идентификатор(), 29 | "query":"Запрос по http протоколу", 30 | "num": д[1], 31 | }, 32 | } 33 | Сообщить("Отправляем:", запр) 34 | Попытка 35 | Сообщить("Ответ",д[1],":",соед.Запрос(запр)) 36 | Исключение 37 | Сообщить(ОписаниеОшибки()) 38 | КонецПопытки 39 | соед.Закрыть() 40 | д[0].Завершить() // группа ожидания 41 | КонецФункции 42 | 43 | Для н=1 по 1000 Цикл 44 | кли = Новый Клиент 45 | гр.Добавить(1) 46 | кли.Открыть("http", "127.0.0.1:9990", фобр, [гр, н]) 47 | КонецЦикла 48 | 49 | гр.Ожидать() 50 | 51 | серв.Закрыть() 52 | 53 | Сообщить("Все завершилось просто идеально за", ПрошлоВремениС(дтнач), "!") 54 | 55 | //асинхронно 56 | гр = Новый ГруппаОжидания 57 | фобр = Функция (соед) 58 | Сообщить("Устанавливаем соединение:",соед) 59 | запр={ 60 | "Метод": "GET", 61 | "Путь": "http://ya.ru", 62 | } 63 | Сообщить("Отправляем:", запр) 64 | Сообщить("Ответ:",соед.Запрос(запр)) 65 | соед.Данные()[0].Завершить() 66 | КонецФункции 67 | 68 | кли = Новый Клиент 69 | гр.Добавить(1) 70 | кли.Открыть("http", "", фобр, [гр]) 71 | гр.Ожидать() 72 | 73 | //синхронно 74 | кли = Новый Клиент 75 | соед = кли.Соединить("http","") 76 | Сообщить("Устанавливаем соединение:",соед) 77 | запр={ 78 | "Метод": "GET", 79 | "Путь": "http://ya.ru", 80 | } 81 | Сообщить("Отправляем:", запр) 82 | Сообщить("Ответ:",соед.Запрос(запр)) 83 | соед.Закрыть() -------------------------------------------------------------------------------- /core/coretable.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | // ТаблицаЗначений 4 | 5 | type VMTableColumn struct { 6 | VMMetaObj 7 | 8 | cols *VMTableColumns 9 | name string 10 | } 11 | 12 | func NewVMTableColumn(vtcs *VMTableColumns) *VMTableColumn { 13 | vtc := &VMTableColumn{ 14 | cols: vtcs, 15 | } 16 | vtc.VMInit(vtc) 17 | vtc.VMRegister() 18 | return vtc 19 | } 20 | 21 | func (vtc *VMTableColumn) VMRegister() { 22 | // vt.VMRegisterField("ПолеСтрока", &vt.ПолеСтрока) 23 | // vt.VMRegisterMethod("Имя", vt.Имя) 24 | } 25 | 26 | type VMTableColumns struct { 27 | VMMetaObj 28 | 29 | table *VMTable 30 | cols []*VMTableColumn 31 | } 32 | 33 | func NewVMTableColumns(vt *VMTable) *VMTableColumns { 34 | vtcs := &VMTableColumns{ 35 | table: vt, 36 | } 37 | vtcs.VMInit(vtcs) 38 | vtcs.VMRegister() 39 | return vtcs 40 | } 41 | 42 | func (vtcs *VMTableColumns) VMRegister() { 43 | vtcs.cols = make([]*VMTableColumn, 0, 8) 44 | // vt.VMRegisterField("ПолеСтрока", &vt.ПолеСтрока) 45 | // vt.VMRegisterMethod("Имя", vt.Имя) 46 | } 47 | 48 | type VMTableLine struct { 49 | VMMetaObj 50 | 51 | table *VMTable 52 | line VMSlice 53 | } 54 | 55 | func NewVMTableLine(vt *VMTable) *VMTableLine { 56 | vtl := &VMTableLine{ 57 | table: vt, 58 | } 59 | vtl.VMInit(vtl) 60 | vtl.VMRegister() 61 | return vtl 62 | } 63 | 64 | func (vtl *VMTableLine) VMRegister() { 65 | // vt.VMRegisterField("ПолеСтрока", &vt.ПолеСтрока) 66 | // vt.VMRegisterMethod("Имя", vt.Имя) 67 | } 68 | 69 | type VMTable struct { 70 | VMMetaObj 71 | 72 | cols *VMTableColumns 73 | lines []*VMTableLine 74 | } 75 | 76 | func (vt *VMTable) VMRegister() { 77 | vt.cols = NewVMTableColumns(vt) 78 | 79 | vt.lines = make([]*VMTableLine, 20) 80 | vt.VMRegisterField("колонки", vt.cols) 81 | 82 | // vt.VMRegisterMethod("колонки", vt.Колонки) 83 | // vt.VMRegisterField("ПолеСтрока", &vt.ПолеСтрока) 84 | } 85 | 86 | func (vt *VMTable) Slice() VMSlice { 87 | rm := make(VMSlice, len(vt.lines)) 88 | for i, v := range vt.lines { 89 | rm[i] = v 90 | } 91 | return rm 92 | } 93 | 94 | func (vt *VMTable) Length() VMInt { 95 | return VMInt(len(vt.lines)) 96 | } 97 | 98 | func (vt *VMTable) IndexVal(idx VMValuer) VMValuer { 99 | if i, ok := idx.(VMInt); ok { 100 | return vt.lines[int(i)] 101 | } 102 | panic("индекс должен быть числом") 103 | } 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Roman TSovanyan rs@tsov.pro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | Copyright (c) 2017 Роман Цованян rs@tsov.pro 24 | 25 | Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации 26 | (в дальнейшем именуемыми «Программное Обеспечение»), безвозмездно использовать Программное Обеспечение без ограничений, 27 | включая неограниченное право на использование, копирование, изменение, слияние, публикацию, распространение, 28 | сублицензирование и/или продажу копий Программного Обеспечения, а также лицам, которым предоставляется данное 29 | Программное Обеспечение, при соблюдении следующих условий: 30 | 31 | Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые 32 | части данного Программного Обеспечения. 33 | 34 | ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, 35 | ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, 36 | НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, 37 | ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, ВОЗНИКШИМ ИЗ-ЗА 38 | ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ. -------------------------------------------------------------------------------- /consulapi/operator_keyring.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | // keyringRequest is used for performing Keyring operations 4 | type keyringRequest struct { 5 | Key string 6 | } 7 | 8 | // KeyringResponse is returned when listing the gossip encryption keys 9 | type KeyringResponse struct { 10 | // Whether this response is for a WAN ring 11 | WAN bool 12 | 13 | // The datacenter name this request corresponds to 14 | Datacenter string 15 | 16 | // Segment has the network segment this request corresponds to. 17 | Segment string 18 | 19 | // A map of the encryption keys to the number of nodes they're installed on 20 | Keys map[string]int 21 | 22 | // The total number of nodes in this ring 23 | NumNodes int 24 | } 25 | 26 | // KeyringInstall is used to install a new gossip encryption key into the cluster 27 | func (op *Operator) KeyringInstall(key string, q *WriteOptions) error { 28 | r := op.c.newRequest("POST", "/v1/operator/keyring") 29 | r.setWriteOptions(q) 30 | r.obj = keyringRequest{ 31 | Key: key, 32 | } 33 | _, resp, err := requireOK(op.c.doRequest(r)) 34 | if err != nil { 35 | return err 36 | } 37 | resp.Body.Close() 38 | return nil 39 | } 40 | 41 | // KeyringList is used to list the gossip keys installed in the cluster 42 | func (op *Operator) KeyringList(q *QueryOptions) ([]*KeyringResponse, error) { 43 | r := op.c.newRequest("GET", "/v1/operator/keyring") 44 | r.setQueryOptions(q) 45 | _, resp, err := requireOK(op.c.doRequest(r)) 46 | if err != nil { 47 | return nil, err 48 | } 49 | defer resp.Body.Close() 50 | 51 | var out []*KeyringResponse 52 | if err := decodeBody(resp, &out); err != nil { 53 | return nil, err 54 | } 55 | return out, nil 56 | } 57 | 58 | // KeyringRemove is used to remove a gossip encryption key from the cluster 59 | func (op *Operator) KeyringRemove(key string, q *WriteOptions) error { 60 | r := op.c.newRequest("DELETE", "/v1/operator/keyring") 61 | r.setWriteOptions(q) 62 | r.obj = keyringRequest{ 63 | Key: key, 64 | } 65 | _, resp, err := requireOK(op.c.doRequest(r)) 66 | if err != nil { 67 | return err 68 | } 69 | resp.Body.Close() 70 | return nil 71 | } 72 | 73 | // KeyringUse is used to change the active gossip encryption key 74 | func (op *Operator) KeyringUse(key string, q *WriteOptions) error { 75 | r := op.c.newRequest("PUT", "/v1/operator/keyring") 76 | r.setWriteOptions(q) 77 | r.obj = keyringRequest{ 78 | Key: key, 79 | } 80 | _, resp, err := requireOK(op.c.doRequest(r)) 81 | if err != nil { 82 | return err 83 | } 84 | resp.Body.Close() 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /bincode/binregs.go: -------------------------------------------------------------------------------- 1 | package bincode 2 | 3 | import ( 4 | "github.com/covrom/gonec/core" 5 | ) 6 | 7 | // Регистры виртуальной машины 8 | 9 | type VMRegs struct { 10 | Env *core.Env 11 | // Reg []core.VMValuer // регистры значений 12 | Labels []int // [label]=index в BinCode 13 | TryLabel []int // последний элемент - это метка на текущий обработчик CATCH 14 | TryRegErr []int // последний элемент - это регистр с ошибкой текущего обработчика 15 | ForBreaks []int // последний элемент - это метка для break 16 | ForContinues []int // последний элемент - это метка для continue 17 | // ReturnTo []int // стек возвратов по RET 18 | } 19 | 20 | // func (v *VMRegs) FreeFromReg(reg int) { 21 | // // освобождаем память, начиная с reg, для сборщика мусора 22 | // // v.Reg = v.Reg[:reg] 23 | // if reg < len(v.Reg) { 24 | // cl := make([]core.VMValuer, len(v.Reg)-reg) 25 | // copy(v.Reg[reg:], cl) 26 | // } 27 | 28 | // // for i := reg; i < len(v.Reg); i++ { 29 | // // v.Reg[i] = nil 30 | // // } 31 | // } 32 | 33 | func (v *VMRegs) PushTry(reg, label int) { 34 | v.TryRegErr = append(v.TryRegErr, reg) 35 | v.TryLabel = append(v.TryLabel, label) 36 | } 37 | 38 | func (v *VMRegs) TopTryLabel() int { 39 | l := len(v.TryLabel) 40 | if l == 0 { 41 | return -1 42 | } 43 | return v.TryLabel[l-1] 44 | } 45 | 46 | func (v *VMRegs) PopTry() (reg int, label int) { 47 | l := len(v.TryLabel) 48 | if l == 0 { 49 | return -1, -1 50 | } 51 | reg = v.TryRegErr[l-1] 52 | v.TryRegErr = v.TryRegErr[0 : l-1] 53 | label = v.TryLabel[l-1] 54 | v.TryLabel = v.TryLabel[0 : l-1] 55 | return 56 | } 57 | 58 | func (v *VMRegs) PushBreak(label int) { 59 | v.ForBreaks = append(v.ForBreaks, label) 60 | } 61 | 62 | func (v *VMRegs) TopBreak() int { 63 | l := len(v.ForBreaks) 64 | if l == 0 { 65 | return -1 66 | } 67 | return v.ForBreaks[l-1] 68 | } 69 | 70 | func (v *VMRegs) PopBreak() (label int) { 71 | l := len(v.ForBreaks) 72 | if l == 0 { 73 | return -1 74 | } 75 | label = v.ForBreaks[l-1] 76 | v.ForBreaks = v.ForBreaks[0 : l-1] 77 | return 78 | } 79 | 80 | func (v *VMRegs) PushContinue(label int) { 81 | v.ForContinues = append(v.ForContinues, label) 82 | } 83 | 84 | func (v *VMRegs) TopContinue() int { 85 | l := len(v.ForContinues) 86 | if l == 0 { 87 | return -1 88 | } 89 | return v.ForContinues[l-1] 90 | } 91 | 92 | func (v *VMRegs) PopContinue() (label int) { 93 | l := len(v.ForContinues) 94 | if l == 0 { 95 | return -1 96 | } 97 | label = v.ForContinues[l-1] 98 | v.ForBreaks = v.ForContinues[0 : l-1] 99 | return 100 | } 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://github.com/covrom/gonec/issues) [](https://github.com/covrom/gonec/releases) 2 | 3 | [](https://github.com/covrom/gonec/releases) 4 | 5 | [](https://gonec.herokuapp.com/) 6 | 7 | [](https://gitpitch.com/covrom/gonec) 8 | [](https://github.com/covrom/gonec/wiki) 9 | 10 | ## Цели 11 | 12 | Платформа `Гонец:Микросервисы` создана для решения программистами 1С задач, связанных с высокопроизводительными распределенными вычислениями, создания микросервисов, вэб-сервисов и вэб-порталов для работы тысяч пользователей, работы с высокоэффективными базами данных, с использованием синтаксиса языка, похожего, но не ограниченного возможностями языка 1С. Для этого интерпретатор встраивается в решения на языке Go. 13 | 14 | ## Описание синтаксиса языка и примеры использования интерпретатора 15 | 16 | [Документация находится здесь](https://github.com/covrom/gonec/wiki) 17 | 18 | Пример приложения "Список задач": 19 | 20 | [](https://github.com/covrom/gonec/wiki/%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80-%D1%81%D0%BF%D0%B8%D1%81%D0%BE%D0%BA-%D0%B7%D0%B0%D0%B4%D0%B0%D1%87) 21 | 22 | ## Масштабируемость языка и платформы 23 | Язык Гонец расширяется путем изменения правил синтаксиса в формате YACC, а так же написания библиотек структур и функций на Го, которые могут быть доступны как объекты метаданных в языке Гонец. 24 | 25 | Посмотреть на использование интерпретатора в роли микросервиса можно по [ссылке](https://gonec.herokuapp.com/) выше. 26 | В этой реализации в интерпретатор встроена простая система запуска кода через обычный браузер. 27 | 28 | ## Какова производительность интерпретатора? 29 | Производительность выше, чем у интерпретатора 1С, и соответствует скорости программ на Go и скорости работы библиотек, написанных на Go. 30 | 31 | Интерпретатор языка использует повторное выделение памяти в синхронизированном пуле, что сокращает расход памяти даже при выполнении глубоких рекурсивных алгоритмов. 32 | 33 | Пример сравнения производительности цикла без тела, перебор значений от 1 до 1 млн. 34 | Участники сравнения: 35 | * Гонец с регистровой виртуальной машиной 36 | * 1С:Предприятие 8.3.9.2170 (файловая) 37 | 38 |  39 | 40 | Производительность одновременного запуска 1000 серверных и 1000 клиентских подключений, по протоколам TCP и HTTP, на 4-ядерном Core-i5 3570 41 | 42 |  43 | 44 | ## Какой статус разработки интерпретатора? 45 | Интерпретатор работает стабильно, протестирован и находится в стадии разработки стандартной библиотеки. 46 | -------------------------------------------------------------------------------- /names/uniquenames.go: -------------------------------------------------------------------------------- 1 | package names 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "sync" 7 | ) 8 | 9 | // все переменные 10 | var UniqueNames = NewEnvNames() 11 | 12 | // уникальные названия переменных, индекс используется в AST-дереве 13 | type EnvNames struct { 14 | mu sync.RWMutex 15 | Names map[string]int 16 | Handles []string 17 | Handlow []string 18 | Iter int 19 | } 20 | 21 | func NewEnvNames() *EnvNames { 22 | en := EnvNames{ 23 | Names: make(map[string]int, 200), 24 | Handles: make([]string, 2, 200), 25 | Handlow: make([]string, 2, 200), 26 | Iter: 1, 27 | } 28 | return &en 29 | } 30 | 31 | func (en *EnvNames) Set(n string) int { 32 | ns := FastToLower(n) 33 | en.mu.RLock() 34 | if i, ok := en.Names[ns]; ok { 35 | en.mu.RUnlock() 36 | return i 37 | } 38 | en.mu.RUnlock() 39 | en.mu.Lock() 40 | i := en.Iter 41 | en.Names[ns] = i 42 | en.Handles[i] = n 43 | en.Handlow[i] = ns 44 | en.Iter++ 45 | for en.Iter >= len(en.Handles) { 46 | en.Handles = append(en.Handles, "") 47 | } 48 | for en.Iter >= len(en.Handlow) { 49 | en.Handlow = append(en.Handlow, "") 50 | } 51 | en.mu.Unlock() 52 | return i 53 | } 54 | 55 | func (en *EnvNames) Get(i int) string { 56 | en.mu.RLock() 57 | defer en.mu.RUnlock() 58 | if i >= 0 && i < len(en.Handles) { 59 | return en.Handles[i] 60 | } else { 61 | panic(fmt.Sprintf("Не найден идентификатор переменной id=%d", i)) 62 | } 63 | } 64 | 65 | func (en *EnvNames) GetLowerCase(i int) string { 66 | en.mu.RLock() 67 | defer en.mu.RUnlock() 68 | if i >= 0 && i < len(en.Handlow) { 69 | return en.Handlow[i] 70 | } else { 71 | panic(fmt.Sprintf("Не найден идентификатор переменной id=%d", i)) 72 | } 73 | } 74 | 75 | func (en *EnvNames) GetLowerCaseOk(i int) (s string, ok bool) { 76 | en.mu.RLock() 77 | defer en.mu.RUnlock() 78 | if i >= 0 && i < len(en.Handlow) { 79 | return en.Handlow[i], true 80 | } else { 81 | return "", false 82 | } 83 | return 84 | } 85 | 86 | func (en *EnvNames) SetToId(n string, i int) { 87 | ns := FastToLower(n) 88 | en.mu.Lock() 89 | en.Names[ns] = i 90 | en.Handles[i] = n 91 | en.Handlow[i] = ns 92 | if en.Iter <= i { 93 | en.Iter = i + 1 // гарантированно следующий 94 | } 95 | for en.Iter >= len(en.Handles) { 96 | en.Handles = append(en.Handles, "") 97 | } 98 | for en.Iter >= len(en.Handlow) { 99 | en.Handlow = append(en.Handlow, "") 100 | } 101 | en.mu.Unlock() 102 | } 103 | 104 | func FastToLower(s string) string { 105 | rs := bytes.NewBuffer(make([]byte, 0, len(s))) 106 | for _, rn := range s { 107 | switch { 108 | case (rn >= 'А' && rn <= 'Я') || (rn >= 'A' && rn <= 'Z'): 109 | rs.WriteRune(rn + 0x20) 110 | case rn == 'Ё': 111 | rs.WriteRune('ё') 112 | default: 113 | rs.WriteRune(rn) 114 | } 115 | } 116 | return rs.String() 117 | } 118 | -------------------------------------------------------------------------------- /consulapi/operator_raft.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | // RaftServer has information about a server in the Raft configuration. 4 | type RaftServer struct { 5 | // ID is the unique ID for the server. These are currently the same 6 | // as the address, but they will be changed to a real GUID in a future 7 | // release of Consul. 8 | ID string 9 | 10 | // Node is the node name of the server, as known by Consul, or this 11 | // will be set to "(unknown)" otherwise. 12 | Node string 13 | 14 | // Address is the IP:port of the server, used for Raft communications. 15 | Address string 16 | 17 | // Leader is true if this server is the current cluster leader. 18 | Leader bool 19 | 20 | // Protocol version is the raft protocol version used by the server 21 | ProtocolVersion string 22 | 23 | // Voter is true if this server has a vote in the cluster. This might 24 | // be false if the server is staging and still coming online, or if 25 | // it's a non-voting server, which will be added in a future release of 26 | // Consul. 27 | Voter bool 28 | } 29 | 30 | // RaftConfiguration is returned when querying for the current Raft configuration. 31 | type RaftConfiguration struct { 32 | // Servers has the list of servers in the Raft configuration. 33 | Servers []*RaftServer 34 | 35 | // Index has the Raft index of this configuration. 36 | Index uint64 37 | } 38 | 39 | // RaftGetConfiguration is used to query the current Raft peer set. 40 | func (op *Operator) RaftGetConfiguration(q *QueryOptions) (*RaftConfiguration, error) { 41 | r := op.c.newRequest("GET", "/v1/operator/raft/configuration") 42 | r.setQueryOptions(q) 43 | _, resp, err := requireOK(op.c.doRequest(r)) 44 | if err != nil { 45 | return nil, err 46 | } 47 | defer resp.Body.Close() 48 | 49 | var out RaftConfiguration 50 | if err := decodeBody(resp, &out); err != nil { 51 | return nil, err 52 | } 53 | return &out, nil 54 | } 55 | 56 | // RaftRemovePeerByAddress is used to kick a stale peer (one that it in the Raft 57 | // quorum but no longer known to Serf or the catalog) by address in the form of 58 | // "IP:port". 59 | func (op *Operator) RaftRemovePeerByAddress(address string, q *WriteOptions) error { 60 | r := op.c.newRequest("DELETE", "/v1/operator/raft/peer") 61 | r.setWriteOptions(q) 62 | 63 | r.params.Set("address", string(address)) 64 | 65 | _, resp, err := requireOK(op.c.doRequest(r)) 66 | if err != nil { 67 | return err 68 | } 69 | 70 | resp.Body.Close() 71 | return nil 72 | } 73 | 74 | // RaftRemovePeerByID is used to kick a stale peer (one that it in the Raft 75 | // quorum but no longer known to Serf or the catalog) by ID. 76 | func (op *Operator) RaftRemovePeerByID(id string, q *WriteOptions) error { 77 | r := op.c.newRequest("DELETE", "/v1/operator/raft/peer") 78 | r.setWriteOptions(q) 79 | 80 | r.params.Set("id", string(id)) 81 | 82 | _, resp, err := requireOK(op.c.doRequest(r)) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | resp.Body.Close() 88 | return nil 89 | } 90 | -------------------------------------------------------------------------------- /core/coreclient.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | type VMClient struct { 9 | VMMetaObj //должен передаваться по ссылке, поэтому это будет объект метаданных 10 | 11 | addr string // [addr]:port 12 | protocol string // tcp, json, http 13 | conn *VMConn // клиент tcp, каждому соединению присваивается GUID 14 | } 15 | 16 | func (x *VMClient) String() string { 17 | return fmt.Sprintf("Клиент %s %s", x.protocol, x.addr) 18 | } 19 | 20 | func (x *VMClient) IsOnline() bool { 21 | return x.conn != nil && !x.conn.closed 22 | } 23 | 24 | func (x *VMClient) Open(proto, addr string, handler VMFunc, data VMValuer, closeOnExitHandler bool) error { 25 | 26 | switch proto { 27 | case "tcp", "tcpzip", "tcptls", "http", "https": 28 | 29 | x.conn = NewVMConn(data) 30 | err := x.conn.Dial(proto, addr, handler, closeOnExitHandler) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | default: 36 | return VMErrorIncorrectProtocol 37 | } 38 | return nil 39 | } 40 | 41 | func (x *VMClient) Close() { 42 | x.conn.Close() 43 | } 44 | 45 | func (x *VMClient) VMRegister() { 46 | x.VMRegisterMethod("Закрыть", x.Закрыть) 47 | x.VMRegisterMethod("Работает", x.Работает) 48 | x.VMRegisterMethod("Открыть", x.Открыть) // асинхронно 49 | x.VMRegisterMethod("Соединить", x.Соединить) // синхронно 50 | 51 | // tst.VMRegisterField("ПолеСтрока", &tst.ПолеСтрока) 52 | } 53 | 54 | func (x *VMClient) Открыть(args VMSlice, rets *VMSlice, envout *(*Env)) error { 55 | if len(args) != 4 { 56 | return VMErrorNeedArgs(4) 57 | } 58 | p, ok := args[0].(VMString) 59 | if !ok { 60 | return errors.New("Первый аргумент должен быть строкой с типом канала") 61 | } 62 | adr, ok := args[1].(VMString) 63 | if !ok { 64 | return errors.New("Второй аргумент должен быть строкой с адресом") 65 | } 66 | f, ok := args[2].(VMFunc) 67 | if !ok { 68 | return errors.New("Третий аргумент должен быть функцией с одним аргументом-соединением") 69 | } 70 | 71 | return x.Open(string(p), string(adr), f, args[3], true) 72 | } 73 | 74 | func (x *VMClient) Соединить(args VMSlice, rets *VMSlice, envout *(*Env)) error { 75 | if len(args) != 2 { 76 | return VMErrorNeedArgs(2) 77 | } 78 | p, ok := args[0].(VMString) 79 | if !ok { 80 | return errors.New("Первый аргумент должен быть строкой с типом канала") 81 | } 82 | adr, ok := args[1].(VMString) 83 | if !ok { 84 | return errors.New("Второй аргумент должен быть строкой с адресом") 85 | } 86 | 87 | err := x.Open(string(p), string(adr), nil, VMNil, false) // не запускает handler 88 | if err != nil { 89 | return err 90 | } 91 | 92 | if x.conn == nil { 93 | return errors.New("Соединение не было установлено") 94 | } 95 | 96 | rets.Append(x.conn) 97 | 98 | return nil 99 | } 100 | 101 | func (x *VMClient) Закрыть(args VMSlice, rets *VMSlice, envout *(*Env)) error { 102 | x.Close() 103 | return nil 104 | } 105 | 106 | func (x *VMClient) Работает(args VMSlice, rets *VMSlice, envout *(*Env)) error { 107 | rets.Append(VMBool(x.IsOnline())) 108 | return nil 109 | } 110 | -------------------------------------------------------------------------------- /consulapi/event.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | import ( 4 | "bytes" 5 | "strconv" 6 | ) 7 | 8 | // Event can be used to query the Event endpoints 9 | type Event struct { 10 | c *Client 11 | } 12 | 13 | // UserEvent represents an event that was fired by the user 14 | type UserEvent struct { 15 | ID string 16 | Name string 17 | Payload []byte 18 | NodeFilter string 19 | ServiceFilter string 20 | TagFilter string 21 | Version int 22 | LTime uint64 23 | } 24 | 25 | // Event returns a handle to the event endpoints 26 | func (c *Client) Event() *Event { 27 | return &Event{c} 28 | } 29 | 30 | // Fire is used to fire a new user event. Only the Name, Payload and Filters 31 | // are respected. This returns the ID or an associated error. Cross DC requests 32 | // are supported. 33 | func (e *Event) Fire(params *UserEvent, q *WriteOptions) (string, *WriteMeta, error) { 34 | r := e.c.newRequest("PUT", "/v1/event/fire/"+params.Name) 35 | r.setWriteOptions(q) 36 | if params.NodeFilter != "" { 37 | r.params.Set("node", params.NodeFilter) 38 | } 39 | if params.ServiceFilter != "" { 40 | r.params.Set("service", params.ServiceFilter) 41 | } 42 | if params.TagFilter != "" { 43 | r.params.Set("tag", params.TagFilter) 44 | } 45 | if params.Payload != nil { 46 | r.body = bytes.NewReader(params.Payload) 47 | } 48 | 49 | rtt, resp, err := requireOK(e.c.doRequest(r)) 50 | if err != nil { 51 | return "", nil, err 52 | } 53 | defer resp.Body.Close() 54 | 55 | wm := &WriteMeta{RequestTime: rtt} 56 | var out UserEvent 57 | if err := decodeBody(resp, &out); err != nil { 58 | return "", nil, err 59 | } 60 | return out.ID, wm, nil 61 | } 62 | 63 | // List is used to get the most recent events an agent has received. 64 | // This list can be optionally filtered by the name. This endpoint supports 65 | // quasi-blocking queries. The index is not monotonic, nor does it provide provide 66 | // LastContact or KnownLeader. 67 | func (e *Event) List(name string, q *QueryOptions) ([]*UserEvent, *QueryMeta, error) { 68 | r := e.c.newRequest("GET", "/v1/event/list") 69 | r.setQueryOptions(q) 70 | if name != "" { 71 | r.params.Set("name", name) 72 | } 73 | rtt, resp, err := requireOK(e.c.doRequest(r)) 74 | if err != nil { 75 | return nil, nil, err 76 | } 77 | defer resp.Body.Close() 78 | 79 | qm := &QueryMeta{} 80 | parseQueryMeta(resp, qm) 81 | qm.RequestTime = rtt 82 | 83 | var entries []*UserEvent 84 | if err := decodeBody(resp, &entries); err != nil { 85 | return nil, nil, err 86 | } 87 | return entries, qm, nil 88 | } 89 | 90 | // IDToIndex is a bit of a hack. This simulates the index generation to 91 | // convert an event ID into a WaitIndex. 92 | func (e *Event) IDToIndex(uuid string) uint64 { 93 | lower := uuid[0:8] + uuid[9:13] + uuid[14:18] 94 | upper := uuid[19:23] + uuid[24:36] 95 | lowVal, err := strconv.ParseUint(lower, 16, 64) 96 | if err != nil { 97 | panic("Failed to convert " + lower) 98 | } 99 | highVal, err := strconv.ParseUint(upper, 16, 64) 100 | if err != nil { 101 | panic("Failed to convert " + upper) 102 | } 103 | return lowVal ^ highVal 104 | } 105 | -------------------------------------------------------------------------------- /tool/makebuiltin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/parser" 7 | "go/token" 8 | "log" 9 | "os" 10 | "os/exec" 11 | "path/filepath" 12 | "sort" 13 | "strings" 14 | ) 15 | 16 | func pkgName(f string) string { 17 | file, err := parser.ParseFile(token.NewFileSet(), f, nil, parser.PackageClauseOnly) 18 | if err != nil || file == nil { 19 | return "" 20 | } 21 | return file.Name.Name 22 | } 23 | 24 | func isGoFile(dir os.FileInfo) bool { 25 | return !dir.IsDir() && 26 | !strings.HasPrefix(dir.Name(), ".") && // ignore .files 27 | filepath.Ext(dir.Name()) == ".go" 28 | } 29 | 30 | func isPkgFile(dir os.FileInfo) bool { 31 | return isGoFile(dir) && !strings.HasSuffix(dir.Name(), "_test.go") // ignore test files 32 | } 33 | 34 | func parseDir(p string) (map[string]*ast.Package, error) { 35 | _, pn := filepath.Split(p) 36 | 37 | isGoDir := func(d os.FileInfo) bool { 38 | if isPkgFile(d) { 39 | name := pkgName(p + "/" + d.Name()) 40 | return name == pn 41 | } 42 | return false 43 | } 44 | 45 | pkgs, err := parser.ParseDir(token.NewFileSet(), p, isGoDir, parser.ParseComments) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return pkgs, nil 50 | } 51 | 52 | func main() { 53 | pkg := "flag" 54 | if len(os.Args) == 2 { 55 | pkg = os.Args[1] 56 | } 57 | b, err := exec.Command("go", "env", "GOROOT").CombinedOutput() 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | paths := []string{filepath.Join(strings.TrimSpace(string(b)), "src")} 62 | b, err = exec.Command("go", "env", "GOPATH").CombinedOutput() 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | for _, p := range strings.Split(strings.TrimSpace(string(b)), string(filepath.ListSeparator)) { 67 | paths = append(paths, filepath.Join(p, "src")) 68 | } 69 | for _, p := range paths { 70 | pp := filepath.Join(p, pkg) 71 | pkgs, err := parseDir(pp) 72 | if err != nil { 73 | continue 74 | } 75 | names := map[string]bool{} 76 | for _, pp := range pkgs { 77 | for _, f := range pp.Files { 78 | for _, d := range f.Decls { 79 | switch decl := d.(type) { 80 | case *ast.GenDecl: 81 | for _, spec := range decl.Specs { 82 | if vspec, ok := spec.(*ast.ValueSpec); ok { 83 | for _, n := range vspec.Names { 84 | c := n.Name[0] 85 | if c < 'A' || c > 'Z' { 86 | continue 87 | } 88 | names[n.Name] = true 89 | } 90 | } 91 | } 92 | case *ast.FuncDecl: 93 | if decl.Recv != nil { 94 | continue 95 | } 96 | c := decl.Name.Name[0] 97 | if c < 'A' || c > 'Z' { 98 | continue 99 | } 100 | names[decl.Name.Name] = true 101 | } 102 | } 103 | } 104 | } 105 | keys := []string{} 106 | for k, _ := range names { 107 | keys = append(keys, k) 108 | } 109 | sort.Strings(keys) 110 | _, pn := filepath.Split(pkg) 111 | fmt.Printf(`// Package %s implements %s interface for gonec script. 112 | package %s 113 | 114 | import ( 115 | envir "github.com/covrom/gonec/core" 116 | pkg "%s" 117 | ) 118 | 119 | func Import(env *envir.Env) *envir.Env { 120 | m := env.NewModule("%s") 121 | `, pn, pkg, pn, pkg, pn) 122 | for _, k := range keys { 123 | fmt.Printf("\t"+`m.Define("%s", pkg.%s)`+"\n", k, k) 124 | } 125 | fmt.Println("\treturn m") 126 | fmt.Println("}") 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /services/gonecsvc/jsAceTheme.go: -------------------------------------------------------------------------------- 1 | package gonecsvc 2 | 3 | const jsAceTheme = `ace.define("ace/theme/tomorrow_night",["require","exports","module","ace/lib/dom"], function(require, exports, module) { 4 | 5 | exports.isDark = true; 6 | exports.cssClass = "ace-tomorrow-night"; 7 | exports.cssText = ".ace-tomorrow-night .ace_gutter {\ 8 | background: #25282c;\ 9 | color: #C5C8C6\ 10 | }\ 11 | .ace-tomorrow-night .ace_print-margin {\ 12 | width: 1px;\ 13 | background: #25282c\ 14 | }\ 15 | .ace-tomorrow-night {\ 16 | background-color: #1D1F21;\ 17 | color: #C5C8C6\ 18 | }\ 19 | .ace-tomorrow-night .ace_cursor {\ 20 | color: #AEAFAD\ 21 | }\ 22 | .ace-tomorrow-night .ace_marker-layer .ace_selection {\ 23 | background: #373B41\ 24 | }\ 25 | .ace-tomorrow-night.ace_multiselect .ace_selection.ace_start {\ 26 | box-shadow: 0 0 3px 0px #1D1F21;\ 27 | }\ 28 | .ace-tomorrow-night .ace_marker-layer .ace_step {\ 29 | background: rgb(102, 82, 0)\ 30 | }\ 31 | .ace-tomorrow-night .ace_marker-layer .ace_bracket {\ 32 | margin: -1px 0 0 -1px;\ 33 | border: 1px solid #4B4E55\ 34 | }\ 35 | .ace-tomorrow-night .ace_marker-layer .ace_active-line {\ 36 | background: #282A2E\ 37 | }\ 38 | .ace-tomorrow-night .ace_gutter-active-line {\ 39 | background-color: #282A2E\ 40 | }\ 41 | .ace-tomorrow-night .ace_marker-layer .ace_selected-word {\ 42 | border: 1px solid #373B41\ 43 | }\ 44 | .ace-tomorrow-night .ace_invisible {\ 45 | color: #4B4E55\ 46 | }\ 47 | .ace-tomorrow-night .ace_keyword,\ 48 | .ace-tomorrow-night .ace_meta,\ 49 | .ace-tomorrow-night .ace_storage,\ 50 | .ace-tomorrow-night .ace_storage.ace_type,\ 51 | .ace-tomorrow-night .ace_support.ace_type {\ 52 | color: #B294BB\ 53 | }\ 54 | .ace-tomorrow-night .ace_keyword.ace_operator {\ 55 | color: #8ABEB7\ 56 | }\ 57 | .ace-tomorrow-night .ace_constant.ace_character,\ 58 | .ace-tomorrow-night .ace_constant.ace_language,\ 59 | .ace-tomorrow-night .ace_constant.ace_numeric,\ 60 | .ace-tomorrow-night .ace_keyword.ace_other.ace_unit,\ 61 | .ace-tomorrow-night .ace_support.ace_constant,\ 62 | .ace-tomorrow-night .ace_variable.ace_parameter {\ 63 | color: #DE935F\ 64 | }\ 65 | .ace-tomorrow-night .ace_constant.ace_other {\ 66 | color: #CED1CF\ 67 | }\ 68 | .ace-tomorrow-night .ace_invalid {\ 69 | color: #CED2CF;\ 70 | background-color: #DF5F5F\ 71 | }\ 72 | .ace-tomorrow-night .ace_invalid.ace_deprecated {\ 73 | color: #CED2CF;\ 74 | background-color: #B798BF\ 75 | }\ 76 | .ace-tomorrow-night .ace_fold {\ 77 | background-color: #81A2BE;\ 78 | border-color: #C5C8C6\ 79 | }\ 80 | .ace-tomorrow-night .ace_entity.ace_name.ace_function,\ 81 | .ace-tomorrow-night .ace_support.ace_function,\ 82 | .ace-tomorrow-night .ace_variable {\ 83 | color: #81A2BE\ 84 | }\ 85 | .ace-tomorrow-night .ace_support.ace_class,\ 86 | .ace-tomorrow-night .ace_support.ace_type {\ 87 | color: #F0C674\ 88 | }\ 89 | .ace-tomorrow-night .ace_heading,\ 90 | .ace-tomorrow-night .ace_markup.ace_heading,\ 91 | .ace-tomorrow-night .ace_string {\ 92 | color: #B5BD68\ 93 | }\ 94 | .ace-tomorrow-night .ace_entity.ace_name.ace_tag,\ 95 | .ace-tomorrow-night .ace_entity.ace_other.ace_attribute-name,\ 96 | .ace-tomorrow-night .ace_meta.ace_tag,\ 97 | .ace-tomorrow-night .ace_string.ace_regexp,\ 98 | .ace-tomorrow-night .ace_variable {\ 99 | color: #CC6666\ 100 | }\ 101 | .ace-tomorrow-night .ace_comment {\ 102 | color: #969896\ 103 | }\ 104 | .ace-tomorrow-night .ace_indent-guide {\ 105 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAACCAYAAACZgbYnAAAAEklEQVQImWNgYGBgYHB3d/8PAAOIAdULw8qMAAAAAElFTkSuQmCC) right repeat-y\ 106 | }"; 107 | 108 | var dom = require("../lib/dom"); 109 | dom.importCssString(exports.cssText, exports.cssClass); 110 | }); 111 | ` -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | digest = "1:0f98f59e9a2f4070d66f0c9c39561f68fcd1dc837b22a852d28d0003aebd1b1e" 6 | name = "github.com/boltdb/bolt" 7 | packages = ["."] 8 | pruneopts = "UT" 9 | revision = "2f1ce7a837dcb8da3ec595b1dac9d0632f0f99e8" 10 | version = "v1.3.1" 11 | 12 | [[projects]] 13 | branch = "master" 14 | digest = "1:473b24ea9f473528968b5a09529280163099631f78e1f4f6588f46f585a45612" 15 | name = "github.com/covrom/decnum" 16 | packages = ["."] 17 | pruneopts = "UT" 18 | revision = "7ae6a5b29284c9c43f93db3bde9081a6b6c41fe7" 19 | 20 | [[projects]] 21 | digest = "1:e921c3d4324ffdcb4fd39d5f47a832f2112d44731a5f21d8ae0ccfe82e608711" 22 | name = "github.com/daviddengcn/go-colortext" 23 | packages = ["."] 24 | pruneopts = "UT" 25 | revision = "17e75f6184bc9e727756cd0d82e0af58b1fc7191" 26 | version = "1.0.0" 27 | 28 | [[projects]] 29 | digest = "1:2915e1464ca9a9480ed2fb94d6939cdf418525b03b24bfb256486990abd8155a" 30 | name = "github.com/dchest/siphash" 31 | packages = ["."] 32 | pruneopts = "UT" 33 | revision = "ca249f45189071f5d44dc6401334e3572037b9cb" 34 | version = "v1.2.0" 35 | 36 | [[projects]] 37 | digest = "1:f47d6109c2034cb16bd62b220e18afd5aa9d5a1630fe5d937ad96a4fb7cbb277" 38 | name = "github.com/hashicorp/go-cleanhttp" 39 | packages = ["."] 40 | pruneopts = "UT" 41 | revision = "e8ab9daed8d1ddd2d3c4efba338fe2eeae2e4f18" 42 | version = "v0.5.0" 43 | 44 | [[projects]] 45 | branch = "master" 46 | digest = "1:45aad874d3c7d5e8610427c81870fb54970b981692930ec2a319ce4cb89d7a00" 47 | name = "github.com/hashicorp/go-rootcerts" 48 | packages = ["."] 49 | pruneopts = "UT" 50 | revision = "6bb64b370b90e7ef1fa532be9e591a81c3493e00" 51 | 52 | [[projects]] 53 | digest = "1:0dd7b7b01769f9df356dc99f9e4144bdbabf6c79041ea7c0892379c5737f3c44" 54 | name = "github.com/hashicorp/serf" 55 | packages = ["coordinate"] 56 | pruneopts = "UT" 57 | revision = "d6574a5bb1226678d7010325fb6c985db20ee458" 58 | version = "v0.8.1" 59 | 60 | [[projects]] 61 | digest = "1:0981502f9816113c9c8c4ac301583841855c8cf4da8c72f696b3ebedf6d0e4e5" 62 | name = "github.com/mattn/go-isatty" 63 | packages = ["."] 64 | pruneopts = "UT" 65 | revision = "6ca4dbf54d38eea1a992b3c722a76a5d1c4cb25c" 66 | version = "v0.0.4" 67 | 68 | [[projects]] 69 | digest = "1:78bbb1ba5b7c3f2ed0ea1eab57bdd3859aec7e177811563edc41198a760b06af" 70 | name = "github.com/mitchellh/go-homedir" 71 | packages = ["."] 72 | pruneopts = "UT" 73 | revision = "ae18d6b8b3205b561c79e8e5f69bff09736185f4" 74 | version = "v1.0.0" 75 | 76 | [[projects]] 77 | digest = "1:274f67cb6fed9588ea2521ecdac05a6d62a8c51c074c1fccc6a49a40ba80e925" 78 | name = "github.com/satori/go.uuid" 79 | packages = ["."] 80 | pruneopts = "UT" 81 | revision = "f58768cc1a7a7e77a3bd49e98cdd21419399b6a3" 82 | version = "v1.2.0" 83 | 84 | [[projects]] 85 | branch = "master" 86 | digest = "1:7ba061af4131fb44b30448572acd0d6fefbf63a61b97b7ef1dea0be5871c2742" 87 | name = "golang.org/x/sys" 88 | packages = ["unix"] 89 | pruneopts = "UT" 90 | revision = "66b7b1311ac80bbafcd2daeef9a5e6e2cd1e2399" 91 | 92 | [solve-meta] 93 | analyzer-name = "dep" 94 | analyzer-version = 1 95 | input-imports = [ 96 | "github.com/boltdb/bolt", 97 | "github.com/covrom/decnum", 98 | "github.com/daviddengcn/go-colortext", 99 | "github.com/dchest/siphash", 100 | "github.com/hashicorp/go-cleanhttp", 101 | "github.com/hashicorp/go-rootcerts", 102 | "github.com/hashicorp/serf/coordinate", 103 | "github.com/mattn/go-isatty", 104 | "github.com/satori/go.uuid", 105 | ] 106 | solver-name = "gps-cdcl" 107 | solver-version = 1 108 | -------------------------------------------------------------------------------- /core/corecrypt.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "compress/flate" 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "crypto/rand" 9 | "crypto/tls" 10 | "io" 11 | "sync" 12 | ) 13 | 14 | var TLSKeyGonec = []byte(`-----BEGIN EC PARAMETERS----- 15 | BgUrgQQAIg== 16 | -----END EC PARAMETERS----- 17 | -----BEGIN EC PRIVATE KEY----- 18 | MIGkAgEBBDCmKNdI+QpNPKaTTkCc+S09XJ+xHPaZwoBCnKP60MjfNf/nYk9rPDKv 19 | AabSoKbY/sWgBwYFK4EEACKhZANiAAR5j/stywkcTihb06Ye+cVFLFCvYCooe1CC 20 | Z8I1BFp+/F4x9zas1NuKVqR3o/CdRBt3pqPtji2NMq5Bq6uKj92yppHhVXiriT19 21 | d2AI06NcTbToyEMa4ookqgIVV3c29/M= 22 | -----END EC PRIVATE KEY----- 23 | `) 24 | 25 | var TLSCertGonec = []byte(`-----BEGIN CERTIFICATE----- 26 | MIICDTCCAZSgAwIBAgIJAI9YNQ6VRzGRMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT 27 | AlJVMRMwEQYDVQQIDApTb21lLVN0YXRlMREwDwYDVQQKDAh0c292LnBybzEOMAwG 28 | A1UEAwwFZ29uZWMwHhcNMTcxMDE3MDYxMjQ2WhcNNDUwMzA0MDYxMjQ2WjBFMQsw 29 | CQYDVQQGEwJSVTETMBEGA1UECAwKU29tZS1TdGF0ZTERMA8GA1UECgwIdHNvdi5w 30 | cm8xDjAMBgNVBAMMBWdvbmVjMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEeY/7LcsJ 31 | HE4oW9OmHvnFRSxQr2AqKHtQgmfCNQRafvxeMfc2rNTbilakd6PwnUQbd6aj7Y4t 32 | jTKuQaurio/dsqaR4VV4q4k9fXdgCNOjXE206MhDGuKKJKoCFVd3Nvfzo1AwTjAd 33 | BgNVHQ4EFgQU6eLQKM4SXqvZQu9zG34zpL9hdEkwHwYDVR0jBBgwFoAU6eLQKM4S 34 | XqvZQu9zG34zpL9hdEkwDAYDVR0TBAUwAwEB/zAKBggqhkjOPQQDAgNnADBkAjAH 35 | T6xYE6AGYbyF2SOt/X+pVo/zI88wzYlFYvx92ozVviLCLzlDZOTFdJkxv7eeetwC 36 | MFWsEfrik+vBTLviWgGqu/y8ESQSyfOnakWE/cbSNnJptU4+iyWcrAKozssX4jEH 37 | qw== 38 | -----END CERTIFICATE----- 39 | `) 40 | 41 | var TLSKeyPair, _ = tls.X509KeyPair(TLSCertGonec, TLSKeyGonec) 42 | 43 | var aesKey = []byte("oUwhsPdfj439pfoi") 44 | 45 | // TODO: перенести в core-функции языка 46 | 47 | func EncryptAES128(plaintext []byte) ([]byte, error) { 48 | c, err := aes.NewCipher(aesKey) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | gcm, err := cipher.NewGCM(c) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | nonce := make([]byte, gcm.NonceSize()) 59 | if _, err = io.ReadFull(rand.Reader, nonce); err != nil { 60 | return nil, err 61 | } 62 | 63 | return gcm.Seal(nonce, nonce, plaintext, nil), nil 64 | } 65 | 66 | func DecryptAES128(ciphertext []byte) ([]byte, error) { 67 | c, err := aes.NewCipher(aesKey) 68 | if err != nil { 69 | return nil, err 70 | } 71 | 72 | gcm, err := cipher.NewGCM(c) 73 | if err != nil { 74 | return nil, err 75 | } 76 | 77 | nonceSize := gcm.NonceSize() 78 | if len(ciphertext) < nonceSize { 79 | return nil, VMErrorSmallDecodeBuffer 80 | } 81 | 82 | nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:] 83 | return gcm.Open(nil, nonce, ciphertext, nil) 84 | } 85 | 86 | var zipPool sync.Pool 87 | 88 | const zipSizeBuf = 32 * 1024 89 | 90 | func getZipBuf() []byte { 91 | sl := zipPool.Get() 92 | if sl != nil { 93 | return sl.([]byte) 94 | } 95 | return make([]byte, zipSizeBuf) 96 | } 97 | 98 | func putZipBuf(sl []byte) { 99 | zipPool.Put(sl) 100 | } 101 | 102 | func GZip(src []byte) ([]byte, error) { 103 | b := bytes.NewBuffer(make([]byte, 0, len(src)*2)) 104 | r := bytes.NewReader(src) 105 | 106 | w, err := flate.NewWriter(b, flate.DefaultCompression) 107 | if err != nil { 108 | return nil, err 109 | } 110 | 111 | buf := getZipBuf() 112 | _, err = io.CopyBuffer(w, r, buf) 113 | putZipBuf(buf) 114 | if err != nil { 115 | return nil, err 116 | } 117 | w.Close() 118 | return b.Bytes(), nil 119 | } 120 | 121 | func UnGZip(src []byte) ([]byte, error) { 122 | b := bytes.NewReader(src) 123 | 124 | r := flate.NewReader(b) 125 | defer r.Close() 126 | 127 | bo := bytes.NewBuffer(make([]byte, 0, len(src)*2)) 128 | buf := getZipBuf() 129 | _, err := io.CopyBuffer(bo, r, buf) 130 | putZipBuf(buf) 131 | if err != nil { 132 | return nil, err 133 | } 134 | return bo.Bytes(), nil 135 | } 136 | -------------------------------------------------------------------------------- /core/coreerrors.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | var ( 9 | VMErrorNeedSinglePacketName = errors.New("Должно быть одно название пакета") 10 | VMErrorNeedLength = errors.New("Значение должно иметь длину") 11 | VMErrorNeedLess = errors.New("Первое значение должно быть меньше второго") 12 | VMErrorNeedLengthOrBoundary = errors.New("Должна быть длина диапазона или начало и конец") 13 | VMErrorNeedFormatAndArgs = errors.New("Должны быть форматная строка и хотя бы один параметр") 14 | VMErrorSmallDecodeBuffer = errors.New("Мало данных для декодирования") 15 | 16 | VMErrorNeedString = errors.New("Требуется значение типа Строка") 17 | VMErrorNeedBool = errors.New("Требуется значение типа Булево") 18 | VMErrorNeedInt = errors.New("Требуется значение типа ЦелоеЧисло") 19 | VMErrorNeedDecNum = errors.New("Требуется значение типа Число") 20 | VMErrorNeedDate = errors.New("Требуется значение типа Дата") 21 | VMErrorNeedMap = errors.New("Требуется значение типа Структура") 22 | VMErrorNeedSlice = errors.New("Требуется значение типа Массив") 23 | VMErrorNeedDuration = errors.New("Требуется значение типа Длительность") 24 | VMErrorNeedSeconds = errors.New("Должно быть число секунд (допустимо с дробной частью)") 25 | VMErrorNeedHash = errors.New("Параметр не может быть хэширован") 26 | VMErrorNeedBinaryTyper = errors.New("Требуется значение, которое может быть сериализовано в бинарное") 27 | 28 | VMErrorIndexOutOfBoundary = errors.New("Индекс находится за пределами массива") 29 | VMErrorNotConverted = errors.New("Приведение к типу невозможно") 30 | VMErrorUnknownType = errors.New("Неизвестный тип данных") 31 | VMErrorIncorrectFieldType = errors.New("Поле структуры имеет другой тип") 32 | VMErrorIncorrectStructType = errors.New("Невозможно использовать данный тип структуры") 33 | VMErrorNotDefined = errors.New("Не определено") 34 | VMErrorNotBinaryConverted = errors.New("Значение не может быть преобразовано в бинарный формат") 35 | 36 | VMErrorNoNeedArgs = errors.New("Параметры не требуются") 37 | VMErrorNoArgs = errors.New("Отсутствуют аргументы") 38 | 39 | VMErrorIncorrectOperation = errors.New("Операция между значениями невозможна") 40 | VMErrorUnknownOperation = errors.New("Неизвестная операция") 41 | 42 | VMErrorServerNowOnline = errors.New("Сервер уже запущен") 43 | VMErrorServerOffline = errors.New("Сервер уже остановлен") 44 | VMErrorIncorrectProtocol = errors.New("Неверный протокол") 45 | VMErrorIncorrectClientId = errors.New("Неверный идентификатор соединения") 46 | VMErrorIncorrectMessage = errors.New("Неверный формат сообщения") 47 | VMErrorEOF = errors.New("Недостаточно данных в источнике") 48 | 49 | VMErrorServiceNotReady = errors.New("Сервис не готов") // устанавливается сервисами в случае прекращения работы 50 | VMErrorServiceAlreadyRegistered = errors.New("Сервис уже зарегистрирован с таким же ID") 51 | VMErrorServerAlreadyStarted = errors.New("Сервер уже запущен") 52 | VMErrorWrongHTTPMethod = errors.New("Метод не применим к HTTP-соединению") 53 | VMErrorNonHTTPMethod = errors.New("Метод применим только к HTTP-соединению") 54 | VMErrorHTTPResponseMethod = errors.New("Метод применим только к ответу HTTP сервера") 55 | VMErrorNilResponse = errors.New("Отсутствует содержимое ответа") 56 | 57 | VMErrorTransactionIsOpened = errors.New("Уже была открыта транзакция") 58 | VMErrorTransactionNotOpened = errors.New("Не открыта транзакция") 59 | VMErrorTableNotExists = errors.New("Отсутствует таблица в базе данных") 60 | VMErrorWrongDBValue = errors.New("Невозможно распознать значение в базе данных") 61 | ) 62 | 63 | func VMErrorNeedArgs(n int) error { 64 | return fmt.Errorf("Неверное количество параметров (требуется %d)", n) 65 | } 66 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "testing" 7 | 8 | "github.com/covrom/gonec/bincode" 9 | "github.com/covrom/gonec/core" 10 | "github.com/covrom/gonec/parser" 11 | ) 12 | 13 | func TestRun(t *testing.T) { 14 | env := core.NewEnv() 15 | 16 | script := ` 17 | дтнач = ТекущаяДата() 18 | а = [](0,1000000) 19 | для н=1 по 1000000 цикл 20 | а=а+[н]+[н*10] 21 | конеццикла 22 | к=0 23 | для каждого н из а цикл 24 | к=к+н 25 | конеццикла 26 | сообщить(к, ПрошлоВремениС(дтнач)) 27 | 28 | #gonec.exe -web -t 29 | #go tool pprof -pdf ./gonec.exe http://localhost:5000/debug/pprof/profile?seconds=60 > cpu.pdf 30 | #go tool pprof -pdf --alloc_objects ./gonec.exe http://localhost:5000/debug/pprof/heap > mem.pdf 31 | 32 | а=Новый("__функциональнаяструктуратест__",{"ПолеСтрока": "цщушаццке", "ПолеЦелоеЧисло": 3456}) 33 | сообщить(а) 34 | сообщить("Хэш", Хэш(а)) 35 | а=Новый("__функциональнаяструктуратест__") 36 | а.Полецелоечисло = 2243 37 | а.ПолеСтрока = "авузлхвсзщл" 38 | сообщить(а) 39 | сообщить("Хэш", Хэш(а)) 40 | сообщить(а.ВСтроку(), а.ПолеСтрока) 41 | Сообщить(Строка(а)) 42 | Сообщить(Структура(Строка(а))) 43 | б=Новый("__ФункциональнаяСтруктураТест__", Строка(а)) //получаем объект из строки json 44 | сообщить("Из json:",б.ВСтроку(), б.ПолеСтрока) 45 | 46 | # массив с вложенным массивом со структурой и датой 47 | а=[2, 1, [3, {"привет":2, "а":1}, Дата("2017-08-17T09:23:00+03:00")], 4] 48 | Сообщить("Исходное:", а, а[2][1].а) 49 | а[2][1].а=Дата("2017-08-17T09:23:00+03:00")+ДлительностьДня*5 50 | # приведение массива или структуры к типу "строка" означает сериализацию в json, со всеми вложенными массивами и структурами 51 | Сообщить("Измененное:", а, а[2][1].а) 52 | Сообщить(Строка(а)) 53 | Сообщить("Ключи в порядке сортировки:", а[2][1].Ключи()) 54 | Сообщить("Формат даты:", а[2][1].а.Формат("дд.ММ.гггг")) 55 | # приведение строки к массиву или структуре означает десериализацию из json 56 | Сообщить(Массив("[1,2,3.5,4]")) 57 | Сообщить(Массив(Строка(а))) 58 | а=[2,1,4.5,3] 59 | Сообщить(а, а[2]*2) 60 | Сообщить(Строка(а)) 61 | а.Сортировать() 62 | а.Обратить() 63 | Сообщить("После сортировки и обращения:",а) 64 | 65 | функция фиб(н) 66 | если н = 0 тогда 67 | возврат 0 68 | иначеесли н = 1 тогда 69 | возврат 1 70 | конецесли 71 | возврат фиб(н-1) + фиб(н-2) 72 | конецфункции 73 | 74 | сообщить(фиб(10)) 75 | 76 | функция фибт(н0, н1, к) 77 | если к = 0 тогда 78 | возврат н0 79 | иначеесли к = 1 тогда 80 | возврат н1 81 | конецесли 82 | возврат фибт(н1, н0+н1, к-1) 83 | конецфункции 84 | 85 | функция фиб2(н) 86 | возврат фибт(0, 1, н) 87 | конецфункции 88 | 89 | сообщить(фиб2(10)) 90 | 91 | функция фиб3(н) 92 | если н = 0 тогда 93 | возврат 0 94 | иначеесли н = 1 тогда 95 | возврат 1 96 | конецесли 97 | н0, н1 = 0, 1 98 | для к = н по 2 цикл 99 | тмп = н0 + н1 100 | н0 = н1 101 | н1 = тмп 102 | конеццикла 103 | возврат н1 104 | конецфункции 105 | 106 | сообщить(фиб3(10)) 107 | 108 | гр = Новый ГруппаОжидания 109 | гр.Добавить(3) 110 | Для н=1 по 3 Цикл 111 | старт Функция(грп,нн) 112 | Сообщить(нн) 113 | грп.Завершить() 114 | КонецФункции(гр,н) 115 | КонецЦикла 116 | гр.Ожидать() 117 | 118 | Функция ОбработатьСерв(вых,вх) 119 | Сообщить("Сервер получил запрос:\n",вх.Сообщение()) 120 | вых.Отправить({"Статус":200, "Тело":"Запрос обработан успешно"}) 121 | КонецФункции 122 | 123 | серв = Новый Сервер 124 | Попытка 125 | серв.Открыть("http", "127.0.0.1:9990", 1000, {"/test":ОбработатьСерв}, 0) 126 | Исключение 127 | Сообщить(ОписаниеОшибки()) 128 | Сообщить("Кажется сервер уже запущен, или тут какая-то другая ошибка, но мы все равно попробуем отправить запрос :)") 129 | КонецПопытки 130 | 131 | //асинхронно 132 | гр = Новый ГруппаОжидания 133 | фобр = Функция (соед) 134 | Сообщить("Устанавливаем соединение:",соед) 135 | запр={ 136 | "Метод": "GET", 137 | "Путь": "http://127.0.0.1:9990/test", 138 | } 139 | Сообщить("Отправляем:", запр) 140 | Сообщить("Ответ:",соед.Запрос(запр)) 141 | соед.Данные()[0].Завершить() 142 | соед.Закрыть() 143 | КонецФункции 144 | 145 | кли = Новый Клиент 146 | гр.Добавить(1) 147 | кли.Открыть("http", "", фобр, [гр]) 148 | гр.Ожидать() 149 | 150 | //синхронно 151 | кли = Новый Клиент 152 | соед = кли.Соединить("http","") 153 | Сообщить("Устанавливаем соединение:",соед) 154 | запр={ 155 | "Метод": "GET", 156 | "Путь": "http://127.0.0.1:9990/test", 157 | } 158 | Сообщить("Отправляем:", запр) 159 | Сообщить("Ответ:",соед.Запрос(запр)) 160 | соед.Закрыть() 161 | кли.Закрыть() 162 | серв.Закрыть() 163 | ` 164 | 165 | parser.EnableErrorVerbose() 166 | _, stmts, err := bincode.ParseSrc(script) 167 | if err != nil { 168 | log.Fatal(err) 169 | } 170 | fmt.Println(stmts) 171 | _, err = bincode.Run(stmts, env) 172 | if err != nil { 173 | log.Fatal(err) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /core/coreifaces.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "encoding" 5 | "reflect" 6 | ) 7 | 8 | // иерархия базовых типов вирт. машины 9 | type ( 10 | // VMValuer корневой тип всех значений, доступных вирт. машине 11 | VMValuer interface { 12 | vmval() 13 | } 14 | 15 | // VMInterfacer корневой тип всех значений, 16 | // которые могут преобразовываться в значения для функций на языке Го в родные типы Го 17 | VMInterfacer interface { 18 | VMValuer 19 | Interface() interface{} // в типах Го, может возвращать в т.ч. nil 20 | } 21 | 22 | // VMFromGoParser может парсить из значений на языке Го 23 | VMFromGoParser interface { 24 | VMValuer 25 | ParseGoType(interface{}) // используется для указателей, т.к. парсит в их значения 26 | } 27 | 28 | // VMOperationer может выполнить операцию с другим значением, операцию сравнения или математическую 29 | VMOperationer interface { 30 | VMValuer 31 | EvalBinOp(VMOperation, VMOperationer) (VMValuer, error) // возвращает результат выражения с другим значением 32 | } 33 | 34 | // VMUnarer может выполнить унарную операцию над свои значением 35 | VMUnarer interface { 36 | VMValuer 37 | EvalUnOp(rune) (VMValuer, error) // возвращает результат выражения с другим значением 38 | } 39 | 40 | // VMConverter может конвертироваться в тип reflect.Type 41 | VMConverter interface { 42 | VMValuer 43 | ConvertToType(t reflect.Type) (VMValuer, error) 44 | } 45 | 46 | // VMChaner реализует поведение канала 47 | VMChaner interface { 48 | VMInterfacer 49 | Send(VMValuer) 50 | Recv() (VMValuer, bool) 51 | TrySend(VMValuer) bool 52 | TryRecv() (VMValuer, bool, bool) 53 | } 54 | 55 | // VMIndexer имеет длину и значение по индексу 56 | VMIndexer interface { 57 | VMInterfacer 58 | Length() VMInt 59 | IndexVal(VMValuer) VMValuer 60 | } 61 | 62 | // VMBinaryTyper может сериализовываться в бинарные данные внутри слайсов и структур 63 | VMBinaryTyper interface { 64 | VMValuer 65 | encoding.BinaryMarshaler 66 | BinaryType() VMBinaryType 67 | } 68 | 69 | // конкретные типы виртуальной машины 70 | 71 | // VMStringer строка 72 | VMStringer interface { 73 | VMInterfacer 74 | String() string 75 | } 76 | 77 | // VMNumberer число, внутреннее хранение в int64 или decimal формате 78 | VMNumberer interface { 79 | VMInterfacer 80 | Int() int64 81 | Float() float64 82 | DecNum() VMDecNum 83 | InvokeNumber() (VMNumberer, error) // извлекает VMInt или VMDecNum, в зависимости от наличия .eE 84 | } 85 | 86 | // VMBooler сообщает значение булево 87 | VMBooler interface { 88 | VMInterfacer 89 | Bool() bool 90 | } 91 | 92 | // VMSlicer может быть представлен в виде слайса Гонец 93 | VMSlicer interface { 94 | VMInterfacer 95 | Slice() VMSlice 96 | } 97 | 98 | // VMStringMaper может быть представлен в виде структуры Гонец 99 | VMStringMaper interface { 100 | VMInterfacer 101 | StringMap() VMStringMap 102 | } 103 | 104 | // VMFuncer это функция Гонец 105 | VMFuncer interface { 106 | VMInterfacer 107 | Func() VMFunc 108 | } 109 | 110 | // VMDateTimer это дата/время 111 | VMDateTimer interface { 112 | VMInterfacer 113 | Time() VMTime 114 | } 115 | 116 | // VMHasher возвращает хэш значения по алгоритму SipHash-2-4 в виде hex-строки 117 | VMHasher interface { 118 | VMInterfacer 119 | Hash() VMString 120 | } 121 | 122 | // VMDurationer это промежуток времени (time.Duration) 123 | VMDurationer interface { 124 | VMInterfacer 125 | Duration() VMTimeDuration 126 | } 127 | 128 | // VMChanMaker может создать новый канал 129 | VMChanMaker interface { 130 | VMInterfacer 131 | MakeChan(int) VMChaner //размер 132 | } 133 | 134 | // VMMetaObject реализует поведение системной функциональной структуры (объекта метаданных) 135 | // реализация должна быть в виде обертки над структурным типом на языке Го 136 | // обертка получается через встраивание базовой структуры VMMetaObj 137 | VMMetaObject interface { 138 | VMInterfacer // реализовано в VMMetaObj 139 | VMInit(VMMetaObject) // реализовано в VMMetaObj 140 | 141 | // !!!эта функция должна быть обязательно реализована в конечном объекте!!! 142 | VMRegister() 143 | 144 | VMRegisterMethod(string, VMMethod) // реализовано в VMMetaObj 145 | VMRegisterField(string, VMValuer) // реализовано в VMMetaObj 146 | 147 | VMIsField(int) bool // реализовано в VMMetaObj 148 | VMGetField(int) VMValuer // реализовано в VMMetaObj 149 | VMSetField(int, VMValuer) // реализовано в VMMetaObj 150 | VMGetMethod(int) (VMFunc, bool) // реализовано в VMMetaObj 151 | } 152 | 153 | // VMMethodImplementer реализует только методы, доступные в языке Гонец 154 | VMMethodImplementer interface{ 155 | VMValuer 156 | MethodMember(int) (VMFunc, bool) // возвращает метод в нужном формате 157 | } 158 | 159 | // VMServicer определяет микросервис, который может регистрироваться в главном менеджере сервисов 160 | VMServicer interface{ 161 | VMValuer 162 | Header() VMServiceHeader 163 | Start() error // запускает горутину, и если не стартовал, возвращает ошибку 164 | HealthCheck() error // если не живой, то возвращает ошибку 165 | Stop() error // последняя ошибка при остановке 166 | } 167 | 168 | // VMNullable означает значение null 169 | VMNullable interface { 170 | VMStringer 171 | null() 172 | } 173 | ) 174 | -------------------------------------------------------------------------------- /core/coreservice.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "runtime" 5 | "strconv" 6 | "sync" 7 | "time" 8 | 9 | "github.com/covrom/gonec/consulapi" 10 | ) 11 | 12 | // VMMainServiceBus главный менеджер сервисов 13 | var VMMainServiceBus = NewServiceBus() 14 | 15 | // VMServiceHeader заголовок сервиса, определяющий его адресуемость 16 | type VMServiceHeader struct { 17 | ID string 18 | Path string // используется в URL сервисов 19 | Port string // число 1-65535 20 | Name string // имя 21 | Tags []string 22 | External string //= "consul" регистрирует сервис в Consul 23 | } 24 | 25 | func NewServiceBus() *VMServiceBus { 26 | v := &VMServiceBus{ 27 | Name: "Главный менеджер сервисов Гонец", 28 | services: make(map[string]VMServicer), 29 | done: make(chan bool), 30 | } 31 | return v 32 | } 33 | 34 | // VMServiceBus внутренний менеджер сервисов 35 | // работает как роутер: открывает порты, обрабатывает пути, вызывает обработчики сервисов 36 | // взаимодействует с внешними service discovery, если требуется 37 | type VMServiceBus struct { 38 | sync.RWMutex 39 | wg sync.WaitGroup 40 | runned bool 41 | 42 | Name string 43 | services map[string]VMServicer //ключ это Id из VMServiceHeader 44 | done chan bool 45 | } 46 | 47 | func (x *VMServiceBus) vmval() {} 48 | 49 | func (x *VMServiceBus) Interface() interface{} { 50 | return x 51 | } 52 | 53 | func (x *VMServiceBus) String() string { 54 | return x.Name 55 | } 56 | 57 | func (x *VMServiceBus) Stop() { 58 | if x.runned { 59 | x.done <- true 60 | } 61 | } 62 | 63 | func (x *VMServiceBus) GetService(id string) (VMServicer, bool) { 64 | x.RLock() 65 | defer x.RUnlock() 66 | 67 | v, ok := x.services[id] 68 | return v, ok 69 | } 70 | 71 | // Run запускает сервис менеджера сервисов 72 | // он каждую секунду стартует зарегистрированные сервисы, если они не прошли HealthCheck 73 | // проверяет остановку менеджера, и если она произошла - останавливает все живые сервисы 74 | func (x *VMServiceBus) Run() { 75 | if x.runned { 76 | return 77 | } 78 | x.wg.Add(1) 79 | x.runned = true 80 | 81 | go func(bus *VMServiceBus) { 82 | defer bus.wg.Done() 83 | defer func() { 84 | bus.runned = false 85 | }() 86 | 87 | for { 88 | bus.RLock() 89 | if len(bus.services) == 0 { 90 | // не осталось ни одного сервиса - выходим 91 | bus.RUnlock() 92 | return 93 | } 94 | for _, svc := range bus.services { 95 | if svc.HealthCheck() != nil { 96 | svc.Start() 97 | } 98 | } 99 | bus.RUnlock() 100 | select { 101 | case <-bus.done: 102 | // останавливаем все живые сервисы 103 | bus.RLock() 104 | for _, svc := range bus.services { 105 | if svc.HealthCheck() == nil { 106 | svc.Stop() 107 | } 108 | } 109 | bus.RUnlock() 110 | return 111 | 112 | case <-time.After(time.Second): 113 | break 114 | } 115 | runtime.Gosched() 116 | } 117 | }(x) 118 | } 119 | 120 | // Register регистрирует сервис в менеджере, дальше с ним можно работать по ID 121 | func (x *VMServiceBus) Register(svc VMServicer) error { 122 | x.Lock() 123 | defer x.Unlock() 124 | 125 | hdr := svc.Header() 126 | if _, ok := x.services[hdr.ID]; ok { 127 | return VMErrorServiceAlreadyRegistered 128 | } 129 | x.services[hdr.ID] = svc 130 | 131 | if hdr.External == "consul" { 132 | 133 | cfg := consulapi.DefaultConfig() 134 | 135 | cli, err := consulapi.NewClient(cfg) 136 | if err != nil { 137 | return err 138 | } 139 | p, err := strconv.Atoi(hdr.Port) 140 | if err != nil { 141 | return err 142 | } 143 | agent := cli.Agent() 144 | if err != nil { 145 | return err 146 | } 147 | 148 | err = agent.ServiceRegister( 149 | &consulapi.AgentServiceRegistration{ 150 | ID: hdr.Path, 151 | Name: hdr.Path, 152 | Port: p, 153 | Check: &consulapi.AgentServiceCheck{ 154 | Interval: "15s", 155 | // чекинг пока идет только по loopback интерфейсу, 156 | // для удаленного надо передавать адреса биндинга из настроек окружения или флагов 157 | HTTP: "http://127.0.0.1:" + hdr.Port + "/" + hdr.Path + "/healthcheck", 158 | }, 159 | }, 160 | ) 161 | if err != nil { 162 | return err 163 | } 164 | } 165 | 166 | return nil 167 | } 168 | 169 | // Register апдейтит сервис в менеджере, останавливая и удаляя старую версию 170 | func (x *VMServiceBus) UpdateRegister(svc VMServicer) error { 171 | 172 | id := svc.Header().ID 173 | 174 | x.RLock() 175 | v, ok := x.services[id] 176 | x.RUnlock() 177 | 178 | if ok { 179 | x.Deregister(v) 180 | } 181 | x.Register(svc) 182 | 183 | return nil 184 | } 185 | 186 | // Register останавливает (если жив) и удаляет сервис из менеджера 187 | func (x *VMServiceBus) Deregister(svc VMServicer) error { 188 | x.Lock() 189 | defer x.Unlock() 190 | 191 | hdr := svc.Header() 192 | v, ok := x.services[hdr.ID] 193 | if ok { 194 | if v.HealthCheck() == nil { 195 | v.Stop() 196 | } 197 | } 198 | delete(x.services, hdr.ID) 199 | 200 | if hdr.External == "consul" { 201 | cli, err := consulapi.NewClient(consulapi.DefaultConfig()) 202 | if err != nil { 203 | return err 204 | } 205 | 206 | agent := cli.Agent() 207 | if err != nil { 208 | return err 209 | } 210 | 211 | err = agent.ServiceDeregister(hdr.Path) 212 | 213 | if err != nil { 214 | return err 215 | } 216 | } 217 | 218 | return nil 219 | } 220 | 221 | // WaitForAll ожидает завершения работы всех сервисов 222 | func (x *VMServiceBus) WaitForAll() { 223 | if x.runned { 224 | x.wg.Wait() 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /consulapi/acl.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | // ACLCLientType is the client type token 9 | ACLClientType = "client" 10 | 11 | // ACLManagementType is the management type token 12 | ACLManagementType = "management" 13 | ) 14 | 15 | // ACLEntry is used to represent an ACL entry 16 | type ACLEntry struct { 17 | CreateIndex uint64 18 | ModifyIndex uint64 19 | ID string 20 | Name string 21 | Type string 22 | Rules string 23 | } 24 | 25 | // ACLReplicationStatus is used to represent the status of ACL replication. 26 | type ACLReplicationStatus struct { 27 | Enabled bool 28 | Running bool 29 | SourceDatacenter string 30 | ReplicatedIndex uint64 31 | LastSuccess time.Time 32 | LastError time.Time 33 | } 34 | 35 | // ACL can be used to query the ACL endpoints 36 | type ACL struct { 37 | c *Client 38 | } 39 | 40 | // ACL returns a handle to the ACL endpoints 41 | func (c *Client) ACL() *ACL { 42 | return &ACL{c} 43 | } 44 | 45 | // Bootstrap is used to perform a one-time ACL bootstrap operation on a cluster 46 | // to get the first management token. 47 | func (a *ACL) Bootstrap() (string, *WriteMeta, error) { 48 | r := a.c.newRequest("PUT", "/v1/acl/bootstrap") 49 | rtt, resp, err := requireOK(a.c.doRequest(r)) 50 | if err != nil { 51 | return "", nil, err 52 | } 53 | defer resp.Body.Close() 54 | 55 | wm := &WriteMeta{RequestTime: rtt} 56 | var out struct{ ID string } 57 | if err := decodeBody(resp, &out); err != nil { 58 | return "", nil, err 59 | } 60 | return out.ID, wm, nil 61 | } 62 | 63 | // Create is used to generate a new token with the given parameters 64 | func (a *ACL) Create(acl *ACLEntry, q *WriteOptions) (string, *WriteMeta, error) { 65 | r := a.c.newRequest("PUT", "/v1/acl/create") 66 | r.setWriteOptions(q) 67 | r.obj = acl 68 | rtt, resp, err := requireOK(a.c.doRequest(r)) 69 | if err != nil { 70 | return "", nil, err 71 | } 72 | defer resp.Body.Close() 73 | 74 | wm := &WriteMeta{RequestTime: rtt} 75 | var out struct{ ID string } 76 | if err := decodeBody(resp, &out); err != nil { 77 | return "", nil, err 78 | } 79 | return out.ID, wm, nil 80 | } 81 | 82 | // Update is used to update the rules of an existing token 83 | func (a *ACL) Update(acl *ACLEntry, q *WriteOptions) (*WriteMeta, error) { 84 | r := a.c.newRequest("PUT", "/v1/acl/update") 85 | r.setWriteOptions(q) 86 | r.obj = acl 87 | rtt, resp, err := requireOK(a.c.doRequest(r)) 88 | if err != nil { 89 | return nil, err 90 | } 91 | defer resp.Body.Close() 92 | 93 | wm := &WriteMeta{RequestTime: rtt} 94 | return wm, nil 95 | } 96 | 97 | // Destroy is used to destroy a given ACL token ID 98 | func (a *ACL) Destroy(id string, q *WriteOptions) (*WriteMeta, error) { 99 | r := a.c.newRequest("PUT", "/v1/acl/destroy/"+id) 100 | r.setWriteOptions(q) 101 | rtt, resp, err := requireOK(a.c.doRequest(r)) 102 | if err != nil { 103 | return nil, err 104 | } 105 | resp.Body.Close() 106 | 107 | wm := &WriteMeta{RequestTime: rtt} 108 | return wm, nil 109 | } 110 | 111 | // Clone is used to return a new token cloned from an existing one 112 | func (a *ACL) Clone(id string, q *WriteOptions) (string, *WriteMeta, error) { 113 | r := a.c.newRequest("PUT", "/v1/acl/clone/"+id) 114 | r.setWriteOptions(q) 115 | rtt, resp, err := requireOK(a.c.doRequest(r)) 116 | if err != nil { 117 | return "", nil, err 118 | } 119 | defer resp.Body.Close() 120 | 121 | wm := &WriteMeta{RequestTime: rtt} 122 | var out struct{ ID string } 123 | if err := decodeBody(resp, &out); err != nil { 124 | return "", nil, err 125 | } 126 | return out.ID, wm, nil 127 | } 128 | 129 | // Info is used to query for information about an ACL token 130 | func (a *ACL) Info(id string, q *QueryOptions) (*ACLEntry, *QueryMeta, error) { 131 | r := a.c.newRequest("GET", "/v1/acl/info/"+id) 132 | r.setQueryOptions(q) 133 | rtt, resp, err := requireOK(a.c.doRequest(r)) 134 | if err != nil { 135 | return nil, nil, err 136 | } 137 | defer resp.Body.Close() 138 | 139 | qm := &QueryMeta{} 140 | parseQueryMeta(resp, qm) 141 | qm.RequestTime = rtt 142 | 143 | var entries []*ACLEntry 144 | if err := decodeBody(resp, &entries); err != nil { 145 | return nil, nil, err 146 | } 147 | if len(entries) > 0 { 148 | return entries[0], qm, nil 149 | } 150 | return nil, qm, nil 151 | } 152 | 153 | // List is used to get all the ACL tokens 154 | func (a *ACL) List(q *QueryOptions) ([]*ACLEntry, *QueryMeta, error) { 155 | r := a.c.newRequest("GET", "/v1/acl/list") 156 | r.setQueryOptions(q) 157 | rtt, resp, err := requireOK(a.c.doRequest(r)) 158 | if err != nil { 159 | return nil, nil, err 160 | } 161 | defer resp.Body.Close() 162 | 163 | qm := &QueryMeta{} 164 | parseQueryMeta(resp, qm) 165 | qm.RequestTime = rtt 166 | 167 | var entries []*ACLEntry 168 | if err := decodeBody(resp, &entries); err != nil { 169 | return nil, nil, err 170 | } 171 | return entries, qm, nil 172 | } 173 | 174 | // Replication returns the status of the ACL replication process in the datacenter 175 | func (a *ACL) Replication(q *QueryOptions) (*ACLReplicationStatus, *QueryMeta, error) { 176 | r := a.c.newRequest("GET", "/v1/acl/replication") 177 | r.setQueryOptions(q) 178 | rtt, resp, err := requireOK(a.c.doRequest(r)) 179 | if err != nil { 180 | return nil, nil, err 181 | } 182 | defer resp.Body.Close() 183 | 184 | qm := &QueryMeta{} 185 | parseQueryMeta(resp, qm) 186 | qm.RequestTime = rtt 187 | 188 | var entries *ACLReplicationStatus 189 | if err := decodeBody(resp, &entries); err != nil { 190 | return nil, nil, err 191 | } 192 | return entries, qm, nil 193 | } 194 | -------------------------------------------------------------------------------- /consulapi/catalog.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | type Node struct { 4 | ID string 5 | Node string 6 | Address string 7 | Datacenter string 8 | TaggedAddresses map[string]string 9 | Meta map[string]string 10 | CreateIndex uint64 11 | ModifyIndex uint64 12 | } 13 | 14 | type CatalogService struct { 15 | ID string 16 | Node string 17 | Address string 18 | Datacenter string 19 | TaggedAddresses map[string]string 20 | NodeMeta map[string]string 21 | ServiceID string 22 | ServiceName string 23 | ServiceAddress string 24 | ServiceTags []string 25 | ServicePort int 26 | ServiceEnableTagOverride bool 27 | CreateIndex uint64 28 | ModifyIndex uint64 29 | } 30 | 31 | type CatalogNode struct { 32 | Node *Node 33 | Services map[string]*AgentService 34 | } 35 | 36 | type CatalogRegistration struct { 37 | ID string 38 | Node string 39 | Address string 40 | TaggedAddresses map[string]string 41 | NodeMeta map[string]string 42 | Datacenter string 43 | Service *AgentService 44 | Check *AgentCheck 45 | } 46 | 47 | type CatalogDeregistration struct { 48 | Node string 49 | Address string // Obsolete. 50 | Datacenter string 51 | ServiceID string 52 | CheckID string 53 | } 54 | 55 | // Catalog can be used to query the Catalog endpoints 56 | type Catalog struct { 57 | c *Client 58 | } 59 | 60 | // Catalog returns a handle to the catalog endpoints 61 | func (c *Client) Catalog() *Catalog { 62 | return &Catalog{c} 63 | } 64 | 65 | func (c *Catalog) Register(reg *CatalogRegistration, q *WriteOptions) (*WriteMeta, error) { 66 | r := c.c.newRequest("PUT", "/v1/catalog/register") 67 | r.setWriteOptions(q) 68 | r.obj = reg 69 | rtt, resp, err := requireOK(c.c.doRequest(r)) 70 | if err != nil { 71 | return nil, err 72 | } 73 | resp.Body.Close() 74 | 75 | wm := &WriteMeta{} 76 | wm.RequestTime = rtt 77 | 78 | return wm, nil 79 | } 80 | 81 | func (c *Catalog) Deregister(dereg *CatalogDeregistration, q *WriteOptions) (*WriteMeta, error) { 82 | r := c.c.newRequest("PUT", "/v1/catalog/deregister") 83 | r.setWriteOptions(q) 84 | r.obj = dereg 85 | rtt, resp, err := requireOK(c.c.doRequest(r)) 86 | if err != nil { 87 | return nil, err 88 | } 89 | resp.Body.Close() 90 | 91 | wm := &WriteMeta{} 92 | wm.RequestTime = rtt 93 | 94 | return wm, nil 95 | } 96 | 97 | // Datacenters is used to query for all the known datacenters 98 | func (c *Catalog) Datacenters() ([]string, error) { 99 | r := c.c.newRequest("GET", "/v1/catalog/datacenters") 100 | _, resp, err := requireOK(c.c.doRequest(r)) 101 | if err != nil { 102 | return nil, err 103 | } 104 | defer resp.Body.Close() 105 | 106 | var out []string 107 | if err := decodeBody(resp, &out); err != nil { 108 | return nil, err 109 | } 110 | return out, nil 111 | } 112 | 113 | // Nodes is used to query all the known nodes 114 | func (c *Catalog) Nodes(q *QueryOptions) ([]*Node, *QueryMeta, error) { 115 | r := c.c.newRequest("GET", "/v1/catalog/nodes") 116 | r.setQueryOptions(q) 117 | rtt, resp, err := requireOK(c.c.doRequest(r)) 118 | if err != nil { 119 | return nil, nil, err 120 | } 121 | defer resp.Body.Close() 122 | 123 | qm := &QueryMeta{} 124 | parseQueryMeta(resp, qm) 125 | qm.RequestTime = rtt 126 | 127 | var out []*Node 128 | if err := decodeBody(resp, &out); err != nil { 129 | return nil, nil, err 130 | } 131 | return out, qm, nil 132 | } 133 | 134 | // Services is used to query for all known services 135 | func (c *Catalog) Services(q *QueryOptions) (map[string][]string, *QueryMeta, error) { 136 | r := c.c.newRequest("GET", "/v1/catalog/services") 137 | r.setQueryOptions(q) 138 | rtt, resp, err := requireOK(c.c.doRequest(r)) 139 | if err != nil { 140 | return nil, nil, err 141 | } 142 | defer resp.Body.Close() 143 | 144 | qm := &QueryMeta{} 145 | parseQueryMeta(resp, qm) 146 | qm.RequestTime = rtt 147 | 148 | var out map[string][]string 149 | if err := decodeBody(resp, &out); err != nil { 150 | return nil, nil, err 151 | } 152 | return out, qm, nil 153 | } 154 | 155 | // Service is used to query catalog entries for a given service 156 | func (c *Catalog) Service(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 157 | r := c.c.newRequest("GET", "/v1/catalog/service/"+service) 158 | r.setQueryOptions(q) 159 | if tag != "" { 160 | r.params.Set("tag", tag) 161 | } 162 | rtt, resp, err := requireOK(c.c.doRequest(r)) 163 | if err != nil { 164 | return nil, nil, err 165 | } 166 | defer resp.Body.Close() 167 | 168 | qm := &QueryMeta{} 169 | parseQueryMeta(resp, qm) 170 | qm.RequestTime = rtt 171 | 172 | var out []*CatalogService 173 | if err := decodeBody(resp, &out); err != nil { 174 | return nil, nil, err 175 | } 176 | return out, qm, nil 177 | } 178 | 179 | // Node is used to query for service information about a single node 180 | func (c *Catalog) Node(node string, q *QueryOptions) (*CatalogNode, *QueryMeta, error) { 181 | r := c.c.newRequest("GET", "/v1/catalog/node/"+node) 182 | r.setQueryOptions(q) 183 | rtt, resp, err := requireOK(c.c.doRequest(r)) 184 | if err != nil { 185 | return nil, nil, err 186 | } 187 | defer resp.Body.Close() 188 | 189 | qm := &QueryMeta{} 190 | parseQueryMeta(resp, qm) 191 | qm.RequestTime = rtt 192 | 193 | var out *CatalogNode 194 | if err := decodeBody(resp, &out); err != nil { 195 | return nil, nil, err 196 | } 197 | return out, qm, nil 198 | } 199 | -------------------------------------------------------------------------------- /core/corebool.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "reflect" 7 | 8 | "github.com/covrom/gonec/names" 9 | ) 10 | 11 | // VMInt для ускорения работы храним целочисленное представление отдельно от decimal 12 | type VMBool bool 13 | 14 | var ReflectVMBool = reflect.TypeOf(VMBool(true)) 15 | 16 | func (x VMBool) vmval() {} 17 | 18 | func (x VMBool) Interface() interface{} { 19 | return bool(x) 20 | } 21 | 22 | func (x *VMBool) ParseGoType(v interface{}) { 23 | switch vv := v.(type) { 24 | case bool: 25 | *x = VMBool(vv) 26 | default: 27 | rv := reflect.Indirect(reflect.ValueOf(v)) 28 | if rv.Kind() == reflect.Interface { 29 | rv = rv.Elem() 30 | } 31 | *x = VMBool(rv.Bool()) // выдаст панику, если это не булево 32 | } 33 | } 34 | 35 | func (x VMBool) String() string { 36 | if x { 37 | return "true" 38 | } else { 39 | return "false" 40 | } 41 | } 42 | 43 | func (x VMBool) Int() int64 { 44 | if x { 45 | return 1 46 | } else { 47 | return 0 48 | } 49 | } 50 | 51 | func (x VMBool) Float() float64 { 52 | if x { 53 | return 1.0 54 | } else { 55 | return 0.0 56 | } 57 | } 58 | 59 | func (x VMBool) DecNum() VMDecNum { 60 | if x { 61 | return VMDecNumOne 62 | } else { 63 | return VMDecNumZero 64 | } 65 | } 66 | 67 | func (x VMBool) Bool() bool { 68 | return bool(x) 69 | } 70 | 71 | func (x VMBool) BinaryType() VMBinaryType { 72 | return VMBOOL 73 | } 74 | 75 | func (x VMBool) MakeChan(size int) VMChaner { 76 | return make(VMChan, size) 77 | } 78 | 79 | func ParseVMBool(s string) (VMBool, error) { 80 | switch names.FastToLower(s) { 81 | case "true", "истина": 82 | return true, nil 83 | case "false", "ложь": 84 | return false, nil 85 | } 86 | return false, VMErrorNotConverted 87 | } 88 | 89 | func (x VMBool) EvalUnOp(op rune) (VMValuer, error) { 90 | switch op { 91 | // case '-': 92 | // case '^': 93 | case '!': 94 | return VMBool(!bool(x)), nil 95 | } 96 | return VMNil, VMErrorUnknownOperation 97 | } 98 | 99 | func (x VMBool) EvalBinOp(op VMOperation, y VMOperationer) (VMValuer, error) { 100 | switch op { 101 | case ADD: 102 | return VMNil, VMErrorIncorrectOperation 103 | case SUB: 104 | return VMNil, VMErrorIncorrectOperation 105 | case MUL: 106 | return VMNil, VMErrorIncorrectOperation 107 | case QUO: 108 | return VMNil, VMErrorIncorrectOperation 109 | case REM: 110 | return VMNil, VMErrorIncorrectOperation 111 | case EQL: 112 | switch yy := y.(type) { 113 | case VMBool: 114 | return VMBool(bool(x) == bool(yy)), nil 115 | } 116 | return VMNil, VMErrorIncorrectOperation 117 | case NEQ: 118 | switch yy := y.(type) { 119 | case VMBool: 120 | return VMBool(bool(x) != bool(yy)), nil 121 | } 122 | return VMNil, VMErrorIncorrectOperation 123 | case GTR: 124 | return VMNil, VMErrorIncorrectOperation 125 | case GEQ: 126 | return VMNil, VMErrorIncorrectOperation 127 | case LSS: 128 | return VMNil, VMErrorIncorrectOperation 129 | case LEQ: 130 | return VMNil, VMErrorIncorrectOperation 131 | case OR: 132 | return VMNil, VMErrorIncorrectOperation 133 | case LOR: 134 | switch yy := y.(type) { 135 | case VMBool: 136 | return VMBool(bool(x) || bool(yy)), nil 137 | } 138 | return VMNil, VMErrorIncorrectOperation 139 | case AND: 140 | return VMNil, VMErrorIncorrectOperation 141 | case LAND: 142 | switch yy := y.(type) { 143 | case VMBool: 144 | return VMBool(bool(x) && bool(yy)), nil 145 | } 146 | return VMNil, VMErrorIncorrectOperation 147 | case POW: 148 | return VMNil, VMErrorIncorrectOperation 149 | case SHR: 150 | return VMNil, VMErrorIncorrectOperation 151 | case SHL: 152 | return VMNil, VMErrorIncorrectOperation 153 | } 154 | return VMNil, VMErrorUnknownOperation 155 | } 156 | 157 | func (x VMBool) ConvertToType(nt reflect.Type) (VMValuer, error) { 158 | switch nt { 159 | case ReflectVMBool: 160 | return x, nil 161 | case ReflectVMString: 162 | return VMString(x.String()), nil 163 | case ReflectVMInt: 164 | return VMInt(x.Int()), nil 165 | // case ReflectVMTime: 166 | case ReflectVMDecNum: 167 | return x.DecNum(), nil 168 | // case ReflectVMSlice: 169 | // case ReflectVMStringMap: 170 | } 171 | 172 | return VMNil, VMErrorNotConverted 173 | } 174 | 175 | func (x VMBool) MarshalBinary() ([]byte, error) { 176 | var buf bytes.Buffer 177 | if bool(x) { 178 | buf.WriteByte(byte(0)) 179 | } else { 180 | buf.WriteByte(byte(1)) 181 | } 182 | return buf.Bytes(), nil 183 | } 184 | 185 | func (x *VMBool) UnmarshalBinary(data []byte) error { 186 | buf := bytes.NewBuffer(data) 187 | if v, err := buf.ReadByte(); err != nil { 188 | return err 189 | } else { 190 | if v == 0 { 191 | *x = VMBool(false) 192 | } else { 193 | *x = VMBool(true) 194 | } 195 | } 196 | return nil 197 | } 198 | 199 | func (x VMBool) GobEncode() ([]byte, error) { 200 | return x.MarshalBinary() 201 | } 202 | 203 | func (x *VMBool) GobDecode(data []byte) error { 204 | return x.UnmarshalBinary(data) 205 | } 206 | 207 | func (x VMBool) MarshalJSON() ([]byte, error) { 208 | return json.Marshal(bool(x)) 209 | } 210 | 211 | func (x *VMBool) UnmarshalJSON(data []byte) error { 212 | if string(data) == "null" { 213 | return nil 214 | } 215 | var b bool 216 | err := json.Unmarshal(data, &b) 217 | if err != nil { 218 | return err 219 | } 220 | *x = VMBool(b) 221 | return nil 222 | } 223 | 224 | func (x VMBool) MarshalText() ([]byte, error) { 225 | return []byte(x.String()), nil 226 | } 227 | 228 | func (x *VMBool) UnmarshalText(data []byte) error { 229 | if string(data) == "null" { 230 | return nil 231 | } 232 | b, err := ParseVMBool(string(data)) 233 | if err != nil { 234 | return err 235 | } 236 | *x = b 237 | return nil 238 | } 239 | -------------------------------------------------------------------------------- /consulapi/health.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | // HealthAny is special, and is used as a wild card, 10 | // not as a specific state. 11 | HealthAny = "any" 12 | HealthPassing = "passing" 13 | HealthWarning = "warning" 14 | HealthCritical = "critical" 15 | HealthMaint = "maintenance" 16 | ) 17 | 18 | const ( 19 | // NodeMaint is the special key set by a node in maintenance mode. 20 | NodeMaint = "_node_maintenance" 21 | 22 | // ServiceMaintPrefix is the prefix for a service in maintenance mode. 23 | ServiceMaintPrefix = "_service_maintenance:" 24 | ) 25 | 26 | // HealthCheck is used to represent a single check 27 | type HealthCheck struct { 28 | Node string 29 | CheckID string 30 | Name string 31 | Status string 32 | Notes string 33 | Output string 34 | ServiceID string 35 | ServiceName string 36 | ServiceTags []string 37 | } 38 | 39 | // HealthChecks is a collection of HealthCheck structs. 40 | type HealthChecks []*HealthCheck 41 | 42 | // AggregatedStatus returns the "best" status for the list of health checks. 43 | // Because a given entry may have many service and node-level health checks 44 | // attached, this function determines the best representative of the status as 45 | // as single string using the following heuristic: 46 | // 47 | // maintenance > critical > warning > passing 48 | // 49 | func (c HealthChecks) AggregatedStatus() string { 50 | var passing, warning, critical, maintenance bool 51 | for _, check := range c { 52 | id := string(check.CheckID) 53 | if id == NodeMaint || strings.HasPrefix(id, ServiceMaintPrefix) { 54 | maintenance = true 55 | continue 56 | } 57 | 58 | switch check.Status { 59 | case HealthPassing: 60 | passing = true 61 | case HealthWarning: 62 | warning = true 63 | case HealthCritical: 64 | critical = true 65 | default: 66 | return "" 67 | } 68 | } 69 | 70 | switch { 71 | case maintenance: 72 | return HealthMaint 73 | case critical: 74 | return HealthCritical 75 | case warning: 76 | return HealthWarning 77 | case passing: 78 | return HealthPassing 79 | default: 80 | return HealthPassing 81 | } 82 | } 83 | 84 | // ServiceEntry is used for the health service endpoint 85 | type ServiceEntry struct { 86 | Node *Node 87 | Service *AgentService 88 | Checks HealthChecks 89 | } 90 | 91 | // Health can be used to query the Health endpoints 92 | type Health struct { 93 | c *Client 94 | } 95 | 96 | // Health returns a handle to the health endpoints 97 | func (c *Client) Health() *Health { 98 | return &Health{c} 99 | } 100 | 101 | // Node is used to query for checks belonging to a given node 102 | func (h *Health) Node(node string, q *QueryOptions) (HealthChecks, *QueryMeta, error) { 103 | r := h.c.newRequest("GET", "/v1/health/node/"+node) 104 | r.setQueryOptions(q) 105 | rtt, resp, err := requireOK(h.c.doRequest(r)) 106 | if err != nil { 107 | return nil, nil, err 108 | } 109 | defer resp.Body.Close() 110 | 111 | qm := &QueryMeta{} 112 | parseQueryMeta(resp, qm) 113 | qm.RequestTime = rtt 114 | 115 | var out HealthChecks 116 | if err := decodeBody(resp, &out); err != nil { 117 | return nil, nil, err 118 | } 119 | return out, qm, nil 120 | } 121 | 122 | // Checks is used to return the checks associated with a service 123 | func (h *Health) Checks(service string, q *QueryOptions) (HealthChecks, *QueryMeta, error) { 124 | r := h.c.newRequest("GET", "/v1/health/checks/"+service) 125 | r.setQueryOptions(q) 126 | rtt, resp, err := requireOK(h.c.doRequest(r)) 127 | if err != nil { 128 | return nil, nil, err 129 | } 130 | defer resp.Body.Close() 131 | 132 | qm := &QueryMeta{} 133 | parseQueryMeta(resp, qm) 134 | qm.RequestTime = rtt 135 | 136 | var out HealthChecks 137 | if err := decodeBody(resp, &out); err != nil { 138 | return nil, nil, err 139 | } 140 | return out, qm, nil 141 | } 142 | 143 | // Service is used to query health information along with service info 144 | // for a given service. It can optionally do server-side filtering on a tag 145 | // or nodes with passing health checks only. 146 | func (h *Health) Service(service, tag string, passingOnly bool, q *QueryOptions) ([]*ServiceEntry, *QueryMeta, error) { 147 | r := h.c.newRequest("GET", "/v1/health/service/"+service) 148 | r.setQueryOptions(q) 149 | if tag != "" { 150 | r.params.Set("tag", tag) 151 | } 152 | if passingOnly { 153 | r.params.Set(HealthPassing, "1") 154 | } 155 | rtt, resp, err := requireOK(h.c.doRequest(r)) 156 | if err != nil { 157 | return nil, nil, err 158 | } 159 | defer resp.Body.Close() 160 | 161 | qm := &QueryMeta{} 162 | parseQueryMeta(resp, qm) 163 | qm.RequestTime = rtt 164 | 165 | var out []*ServiceEntry 166 | if err := decodeBody(resp, &out); err != nil { 167 | return nil, nil, err 168 | } 169 | return out, qm, nil 170 | } 171 | 172 | // State is used to retrieve all the checks in a given state. 173 | // The wildcard "any" state can also be used for all checks. 174 | func (h *Health) State(state string, q *QueryOptions) (HealthChecks, *QueryMeta, error) { 175 | switch state { 176 | case HealthAny: 177 | case HealthWarning: 178 | case HealthCritical: 179 | case HealthPassing: 180 | default: 181 | return nil, nil, fmt.Errorf("Unsupported state: %v", state) 182 | } 183 | r := h.c.newRequest("GET", "/v1/health/state/"+state) 184 | r.setQueryOptions(q) 185 | rtt, resp, err := requireOK(h.c.doRequest(r)) 186 | if err != nil { 187 | return nil, nil, err 188 | } 189 | defer resp.Body.Close() 190 | 191 | qm := &QueryMeta{} 192 | parseQueryMeta(resp, qm) 193 | qm.RequestTime = rtt 194 | 195 | var out HealthChecks 196 | if err := decodeBody(resp, &out); err != nil { 197 | return nil, nil, err 198 | } 199 | return out, qm, nil 200 | } 201 | -------------------------------------------------------------------------------- /consulapi/operator_area.go: -------------------------------------------------------------------------------- 1 | // The /v1/operator/area endpoints are available only in Consul Enterprise and 2 | // interact with its network area subsystem. Network areas are used to link 3 | // together Consul servers in different Consul datacenters. With network areas, 4 | // Consul datacenters can be linked together in ways other than a fully-connected 5 | // mesh, as is required for Consul's WAN. 6 | package consulapi 7 | 8 | import ( 9 | "net" 10 | "time" 11 | ) 12 | 13 | // Area defines a network area. 14 | type Area struct { 15 | // ID is this identifier for an area (a UUID). This must be left empty 16 | // when creating a new area. 17 | ID string 18 | 19 | // PeerDatacenter is the peer Consul datacenter that will make up the 20 | // other side of this network area. Network areas always involve a pair 21 | // of datacenters: the datacenter where the area was created, and the 22 | // peer datacenter. This is required. 23 | PeerDatacenter string 24 | 25 | // RetryJoin specifies the address of Consul servers to join to, such as 26 | // an IPs or hostnames with an optional port number. This is optional. 27 | RetryJoin []string 28 | 29 | // UseTLS specifies whether gossip over this area should be encrypted with TLS 30 | // if possible. 31 | UseTLS bool 32 | } 33 | 34 | // AreaJoinResponse is returned when a join occurs and gives the result for each 35 | // address. 36 | type AreaJoinResponse struct { 37 | // The address that was joined. 38 | Address string 39 | 40 | // Whether or not the join was a success. 41 | Joined bool 42 | 43 | // If we couldn't join, this is the message with information. 44 | Error string 45 | } 46 | 47 | // SerfMember is a generic structure for reporting information about members in 48 | // a Serf cluster. This is only used by the area endpoints right now, but this 49 | // could be expanded to other endpoints in the future. 50 | type SerfMember struct { 51 | // ID is the node identifier (a UUID). 52 | ID string 53 | 54 | // Name is the node name. 55 | Name string 56 | 57 | // Addr has the IP address. 58 | Addr net.IP 59 | 60 | // Port is the RPC port. 61 | Port uint16 62 | 63 | // Datacenter is the DC name. 64 | Datacenter string 65 | 66 | // Role is "client", "server", or "unknown". 67 | Role string 68 | 69 | // Build has the version of the Consul agent. 70 | Build string 71 | 72 | // Protocol is the protocol of the Consul agent. 73 | Protocol int 74 | 75 | // Status is the Serf health status "none", "alive", "leaving", "left", 76 | // or "failed". 77 | Status string 78 | 79 | // RTT is the estimated round trip time from the server handling the 80 | // request to the this member. This will be negative if no RTT estimate 81 | // is available. 82 | RTT time.Duration 83 | } 84 | 85 | // AreaCreate will create a new network area. The ID in the given structure must 86 | // be empty and a generated ID will be returned on success. 87 | func (op *Operator) AreaCreate(area *Area, q *WriteOptions) (string, *WriteMeta, error) { 88 | r := op.c.newRequest("POST", "/v1/operator/area") 89 | r.setWriteOptions(q) 90 | r.obj = area 91 | rtt, resp, err := requireOK(op.c.doRequest(r)) 92 | if err != nil { 93 | return "", nil, err 94 | } 95 | defer resp.Body.Close() 96 | 97 | wm := &WriteMeta{} 98 | wm.RequestTime = rtt 99 | 100 | var out struct{ ID string } 101 | if err := decodeBody(resp, &out); err != nil { 102 | return "", nil, err 103 | } 104 | return out.ID, wm, nil 105 | } 106 | 107 | // AreaUpdate will update the configuration of the network area with the given ID. 108 | func (op *Operator) AreaUpdate(areaID string, area *Area, q *WriteOptions) (string, *WriteMeta, error) { 109 | r := op.c.newRequest("PUT", "/v1/operator/area/"+areaID) 110 | r.setWriteOptions(q) 111 | r.obj = area 112 | rtt, resp, err := requireOK(op.c.doRequest(r)) 113 | if err != nil { 114 | return "", nil, err 115 | } 116 | defer resp.Body.Close() 117 | 118 | wm := &WriteMeta{} 119 | wm.RequestTime = rtt 120 | 121 | var out struct{ ID string } 122 | if err := decodeBody(resp, &out); err != nil { 123 | return "", nil, err 124 | } 125 | return out.ID, wm, nil 126 | } 127 | 128 | // AreaGet returns a single network area. 129 | func (op *Operator) AreaGet(areaID string, q *QueryOptions) ([]*Area, *QueryMeta, error) { 130 | var out []*Area 131 | qm, err := op.c.query("/v1/operator/area/"+areaID, &out, q) 132 | if err != nil { 133 | return nil, nil, err 134 | } 135 | return out, qm, nil 136 | } 137 | 138 | // AreaList returns all the available network areas. 139 | func (op *Operator) AreaList(q *QueryOptions) ([]*Area, *QueryMeta, error) { 140 | var out []*Area 141 | qm, err := op.c.query("/v1/operator/area", &out, q) 142 | if err != nil { 143 | return nil, nil, err 144 | } 145 | return out, qm, nil 146 | } 147 | 148 | // AreaDelete deletes the given network area. 149 | func (op *Operator) AreaDelete(areaID string, q *WriteOptions) (*WriteMeta, error) { 150 | r := op.c.newRequest("DELETE", "/v1/operator/area/"+areaID) 151 | r.setWriteOptions(q) 152 | rtt, resp, err := requireOK(op.c.doRequest(r)) 153 | if err != nil { 154 | return nil, err 155 | } 156 | defer resp.Body.Close() 157 | 158 | wm := &WriteMeta{} 159 | wm.RequestTime = rtt 160 | return wm, nil 161 | } 162 | 163 | // AreaJoin attempts to join the given set of join addresses to the given 164 | // network area. See the Area structure for details about join addresses. 165 | func (op *Operator) AreaJoin(areaID string, addresses []string, q *WriteOptions) ([]*AreaJoinResponse, *WriteMeta, error) { 166 | r := op.c.newRequest("PUT", "/v1/operator/area/"+areaID+"/join") 167 | r.setWriteOptions(q) 168 | r.obj = addresses 169 | rtt, resp, err := requireOK(op.c.doRequest(r)) 170 | if err != nil { 171 | return nil, nil, err 172 | } 173 | defer resp.Body.Close() 174 | 175 | wm := &WriteMeta{} 176 | wm.RequestTime = rtt 177 | 178 | var out []*AreaJoinResponse 179 | if err := decodeBody(resp, &out); err != nil { 180 | return nil, nil, err 181 | } 182 | return out, wm, nil 183 | } 184 | 185 | // AreaMembers lists the Serf information about the members in the given area. 186 | func (op *Operator) AreaMembers(areaID string, q *QueryOptions) ([]*SerfMember, *QueryMeta, error) { 187 | var out []*SerfMember 188 | qm, err := op.c.query("/v1/operator/area/"+areaID+"/members", &out, q) 189 | if err != nil { 190 | return nil, nil, err 191 | } 192 | return out, qm, nil 193 | } 194 | -------------------------------------------------------------------------------- /consulapi/session.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | const ( 10 | // SessionBehaviorRelease is the default behavior and causes 11 | // all associated locks to be released on session invalidation. 12 | SessionBehaviorRelease = "release" 13 | 14 | // SessionBehaviorDelete is new in Consul 0.5 and changes the 15 | // behavior to delete all associated locks on session invalidation. 16 | // It can be used in a way similar to Ephemeral Nodes in ZooKeeper. 17 | SessionBehaviorDelete = "delete" 18 | ) 19 | 20 | var ErrSessionExpired = errors.New("session expired") 21 | 22 | // SessionEntry represents a session in consul 23 | type SessionEntry struct { 24 | CreateIndex uint64 25 | ID string 26 | Name string 27 | Node string 28 | Checks []string 29 | LockDelay time.Duration 30 | Behavior string 31 | TTL string 32 | } 33 | 34 | // Session can be used to query the Session endpoints 35 | type Session struct { 36 | c *Client 37 | } 38 | 39 | // Session returns a handle to the session endpoints 40 | func (c *Client) Session() *Session { 41 | return &Session{c} 42 | } 43 | 44 | // CreateNoChecks is like Create but is used specifically to create 45 | // a session with no associated health checks. 46 | func (s *Session) CreateNoChecks(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) { 47 | body := make(map[string]interface{}) 48 | body["Checks"] = []string{} 49 | if se != nil { 50 | if se.Name != "" { 51 | body["Name"] = se.Name 52 | } 53 | if se.Node != "" { 54 | body["Node"] = se.Node 55 | } 56 | if se.LockDelay != 0 { 57 | body["LockDelay"] = durToMsec(se.LockDelay) 58 | } 59 | if se.Behavior != "" { 60 | body["Behavior"] = se.Behavior 61 | } 62 | if se.TTL != "" { 63 | body["TTL"] = se.TTL 64 | } 65 | } 66 | return s.create(body, q) 67 | 68 | } 69 | 70 | // Create makes a new session. Providing a session entry can 71 | // customize the session. It can also be nil to use defaults. 72 | func (s *Session) Create(se *SessionEntry, q *WriteOptions) (string, *WriteMeta, error) { 73 | var obj interface{} 74 | if se != nil { 75 | body := make(map[string]interface{}) 76 | obj = body 77 | if se.Name != "" { 78 | body["Name"] = se.Name 79 | } 80 | if se.Node != "" { 81 | body["Node"] = se.Node 82 | } 83 | if se.LockDelay != 0 { 84 | body["LockDelay"] = durToMsec(se.LockDelay) 85 | } 86 | if len(se.Checks) > 0 { 87 | body["Checks"] = se.Checks 88 | } 89 | if se.Behavior != "" { 90 | body["Behavior"] = se.Behavior 91 | } 92 | if se.TTL != "" { 93 | body["TTL"] = se.TTL 94 | } 95 | } 96 | return s.create(obj, q) 97 | } 98 | 99 | func (s *Session) create(obj interface{}, q *WriteOptions) (string, *WriteMeta, error) { 100 | var out struct{ ID string } 101 | wm, err := s.c.write("/v1/session/create", obj, &out, q) 102 | if err != nil { 103 | return "", nil, err 104 | } 105 | return out.ID, wm, nil 106 | } 107 | 108 | // Destroy invalidates a given session 109 | func (s *Session) Destroy(id string, q *WriteOptions) (*WriteMeta, error) { 110 | wm, err := s.c.write("/v1/session/destroy/"+id, nil, nil, q) 111 | if err != nil { 112 | return nil, err 113 | } 114 | return wm, nil 115 | } 116 | 117 | // Renew renews the TTL on a given session 118 | func (s *Session) Renew(id string, q *WriteOptions) (*SessionEntry, *WriteMeta, error) { 119 | r := s.c.newRequest("PUT", "/v1/session/renew/"+id) 120 | r.setWriteOptions(q) 121 | rtt, resp, err := s.c.doRequest(r) 122 | if err != nil { 123 | return nil, nil, err 124 | } 125 | defer resp.Body.Close() 126 | 127 | wm := &WriteMeta{RequestTime: rtt} 128 | 129 | if resp.StatusCode == 404 { 130 | return nil, wm, nil 131 | } else if resp.StatusCode != 200 { 132 | return nil, nil, fmt.Errorf("Unexpected response code: %d", resp.StatusCode) 133 | } 134 | 135 | var entries []*SessionEntry 136 | if err := decodeBody(resp, &entries); err != nil { 137 | return nil, nil, fmt.Errorf("Failed to read response: %v", err) 138 | } 139 | if len(entries) > 0 { 140 | return entries[0], wm, nil 141 | } 142 | return nil, wm, nil 143 | } 144 | 145 | // RenewPeriodic is used to periodically invoke Session.Renew on a 146 | // session until a doneCh is closed. This is meant to be used in a long running 147 | // goroutine to ensure a session stays valid. 148 | func (s *Session) RenewPeriodic(initialTTL string, id string, q *WriteOptions, doneCh <-chan struct{}) error { 149 | ctx := q.Context() 150 | 151 | ttl, err := time.ParseDuration(initialTTL) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | waitDur := ttl / 2 157 | lastRenewTime := time.Now() 158 | var lastErr error 159 | for { 160 | if time.Since(lastRenewTime) > ttl { 161 | return lastErr 162 | } 163 | select { 164 | case <-time.After(waitDur): 165 | entry, _, err := s.Renew(id, q) 166 | if err != nil { 167 | waitDur = time.Second 168 | lastErr = err 169 | continue 170 | } 171 | if entry == nil { 172 | return ErrSessionExpired 173 | } 174 | 175 | // Handle the server updating the TTL 176 | ttl, _ = time.ParseDuration(entry.TTL) 177 | waitDur = ttl / 2 178 | lastRenewTime = time.Now() 179 | 180 | case <-doneCh: 181 | // Attempt a session destroy 182 | s.Destroy(id, q) 183 | return nil 184 | 185 | case <-ctx.Done(): 186 | // Bail immediately since attempting the destroy would 187 | // use the canceled context in q, which would just bail. 188 | return ctx.Err() 189 | } 190 | } 191 | } 192 | 193 | // Info looks up a single session 194 | func (s *Session) Info(id string, q *QueryOptions) (*SessionEntry, *QueryMeta, error) { 195 | var entries []*SessionEntry 196 | qm, err := s.c.query("/v1/session/info/"+id, &entries, q) 197 | if err != nil { 198 | return nil, nil, err 199 | } 200 | if len(entries) > 0 { 201 | return entries[0], qm, nil 202 | } 203 | return nil, qm, nil 204 | } 205 | 206 | // List gets sessions for a node 207 | func (s *Session) Node(node string, q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) { 208 | var entries []*SessionEntry 209 | qm, err := s.c.query("/v1/session/node/"+node, &entries, q) 210 | if err != nil { 211 | return nil, nil, err 212 | } 213 | return entries, qm, nil 214 | } 215 | 216 | // List gets all active sessions 217 | func (s *Session) List(q *QueryOptions) ([]*SessionEntry, *QueryMeta, error) { 218 | var entries []*SessionEntry 219 | qm, err := s.c.query("/v1/session/list", &entries, q) 220 | if err != nil { 221 | return nil, nil, err 222 | } 223 | return entries, qm, nil 224 | } 225 | -------------------------------------------------------------------------------- /core/coremetaobj.go: -------------------------------------------------------------------------------- 1 | package core 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/gob" 7 | "encoding/hex" 8 | "encoding/json" 9 | "reflect" 10 | 11 | "github.com/covrom/gonec/names" 12 | ) 13 | 14 | // VMMetaObj корневая структура для системных функциональных структур Го, доступных из языка Гонец 15 | // поля и методы должны отличаться друг от друга без учета регистра 16 | // например, Set и set - в вирт. машине будут считаться одинаковыми, будет использоваться последнее по индексу 17 | type VMMetaObj struct { 18 | vmMetaCacheM map[int]VMFunc 19 | vmMetaCacheF map[int]VMValuer 20 | 21 | vmOriginal VMMetaObject 22 | } 23 | 24 | func (v *VMMetaObj) vmval() {} 25 | 26 | func (v *VMMetaObj) VMInit(m VMMetaObject) { 27 | // исходная структура 28 | v.vmOriginal = m 29 | } 30 | 31 | func (v *VMMetaObj) Interface() interface{} { 32 | // возвращает ссылку на структуру, от которой был вызван метод VMInit 33 | //rv:=*(*VMMetaObject)(v.vmOriginal) 34 | return v.vmOriginal 35 | } 36 | 37 | func (v *VMMetaObj) VMRegister() {} // не забывать реализовывать этот метод в содержащих VMMetaObj структурах! 38 | 39 | func (v *VMMetaObj) String() string { 40 | b, err := json.Marshal(v.vmOriginal) 41 | if err != nil { 42 | panic(err) 43 | } 44 | return string(b) 45 | } 46 | 47 | func (x *VMMetaObj) Hash() VMString { 48 | b := []byte(x.String()) 49 | h := make([]byte, 8) 50 | binary.LittleEndian.PutUint64(h, HashBytes(b)) 51 | return VMString(hex.EncodeToString(h)) 52 | } 53 | 54 | func (v *VMMetaObj) VMRegisterMethod(name string, m VMMethod) { 55 | if v.vmMetaCacheM == nil { 56 | v.vmMetaCacheM = make(map[int]VMFunc) 57 | } 58 | namtyp := names.UniqueNames.Set(name) 59 | v.vmMetaCacheM[namtyp] = func(meth VMMethod) VMFunc { 60 | return VMFunc(meth) 61 | }(m) 62 | } 63 | 64 | func (v *VMMetaObj) VMRegisterField(name string, m VMValuer) { 65 | if v.vmMetaCacheF == nil { 66 | v.vmMetaCacheF = make(map[int]VMValuer) 67 | } 68 | switch m.(type) { 69 | case *VMInt, *VMString, *VMBool, 70 | *VMChan, *VMDecNum, *VMStringMap, 71 | *VMSlice, *VMTime, *VMTimeDuration: 72 | 73 | namtyp := names.UniqueNames.Set(name) 74 | v.vmMetaCacheF[namtyp] = m 75 | default: 76 | panic("Поле не может быть зарегистрировано") 77 | } 78 | } 79 | 80 | func (v *VMMetaObj) VMIsField(name int) bool { 81 | _, ok := v.vmMetaCacheF[name] 82 | return ok 83 | } 84 | 85 | func (v *VMMetaObj) VMGetField(name int) VMValuer { 86 | if r, ok := v.vmMetaCacheF[name]; ok { 87 | switch rv := r.(type) { 88 | case *VMInt: 89 | return *rv 90 | case *VMString: 91 | return *rv 92 | case *VMBool: 93 | return *rv 94 | case *VMChan: 95 | return *rv 96 | case *VMDecNum: 97 | return *rv 98 | case *VMStringMap: 99 | return *rv 100 | case *VMSlice: 101 | return *rv 102 | case *VMTime: 103 | return *rv 104 | case *VMTimeDuration: 105 | return *rv 106 | } 107 | } 108 | panic("Невозможно получить значение поля") 109 | } 110 | 111 | func (v *VMMetaObj) VMSetField(name int, val VMValuer) { 112 | 113 | if r, ok := v.vmMetaCacheF[name]; ok { 114 | switch rv := r.(type) { 115 | case *VMInt: 116 | *rv = VMInt(val.(VMNumberer).Int()) 117 | return 118 | case *VMString: 119 | *rv = VMString(val.(VMStringer).String()) 120 | return 121 | case *VMBool: 122 | *rv = VMBool(val.(VMBooler).Bool()) 123 | return 124 | case *VMChan: 125 | *rv = val.(VMChan) 126 | return 127 | case *VMDecNum: 128 | *rv = val.(VMNumberer).DecNum() 129 | return 130 | case *VMStringMap: 131 | *rv = val.(VMStringMaper).StringMap() 132 | return 133 | case *VMSlice: 134 | *rv = val.(VMSlicer).Slice() 135 | return 136 | case *VMTime: 137 | *rv = val.(VMDateTimer).Time() 138 | return 139 | case *VMTimeDuration: 140 | *rv = val.(VMDurationer).Duration() 141 | return 142 | } 143 | } 144 | 145 | panic("Невозможно установить значение поля") 146 | } 147 | 148 | // VMGetMethod генерит функцию, 149 | // которая возвращает либо одно значение и ошибку, либо массив значений интерпретатора VMSlice 150 | func (v *VMMetaObj) VMGetMethod(name int) (VMFunc, bool) { 151 | 152 | // fmt.Println(name) 153 | 154 | rv, ok := v.vmMetaCacheM[name] 155 | return rv, ok 156 | } 157 | 158 | func (v *VMMetaObj) EvalBinOp(op VMOperation, y VMOperationer) (VMValuer, error) { 159 | switch op { 160 | case ADD: 161 | return VMNil, VMErrorIncorrectOperation 162 | case SUB: 163 | return VMNil, VMErrorIncorrectOperation 164 | case MUL: 165 | return VMNil, VMErrorIncorrectOperation 166 | case QUO: 167 | return VMNil, VMErrorIncorrectOperation 168 | case REM: 169 | return VMNil, VMErrorIncorrectOperation 170 | case EQL: 171 | switch yy := y.(type) { 172 | case *VMMetaObj: 173 | eq := v.Hash() == yy.Hash() 174 | return VMBool(eq), nil 175 | } 176 | return VMNil, VMErrorIncorrectOperation 177 | case NEQ: 178 | switch yy := y.(type) { 179 | case *VMMetaObj: 180 | eq := v.Hash() != yy.Hash() 181 | return VMBool(eq), nil 182 | } 183 | return VMNil, VMErrorIncorrectOperation 184 | case GTR: 185 | return VMNil, VMErrorIncorrectOperation 186 | case GEQ: 187 | return VMNil, VMErrorIncorrectOperation 188 | case LSS: 189 | return VMNil, VMErrorIncorrectOperation 190 | case LEQ: 191 | return VMNil, VMErrorIncorrectOperation 192 | case OR: 193 | return VMNil, VMErrorIncorrectOperation 194 | case LOR: 195 | return VMNil, VMErrorIncorrectOperation 196 | case AND: 197 | return VMNil, VMErrorIncorrectOperation 198 | case LAND: 199 | return VMNil, VMErrorIncorrectOperation 200 | case POW: 201 | return VMNil, VMErrorIncorrectOperation 202 | case SHR: 203 | return VMNil, VMErrorIncorrectOperation 204 | case SHL: 205 | return VMNil, VMErrorIncorrectOperation 206 | } 207 | return VMNil, VMErrorUnknownOperation 208 | } 209 | 210 | func (v *VMMetaObj) ConvertToType(nt reflect.Type) (VMValuer, error) { 211 | switch nt { 212 | case ReflectVMString: 213 | // сериализуем в json 214 | b, err := json.Marshal(v.vmOriginal) 215 | if err != nil { 216 | return VMNil, err 217 | } 218 | return VMString(string(b)), nil 219 | // case ReflectVMInt: 220 | // case ReflectVMTime: 221 | // case ReflectVMBool: 222 | // case ReflectVMDecNum: 223 | // case ReflectVMSlice: 224 | // case ReflectVMStringMap: // получится только через Структура(Строка(объект)) 225 | } 226 | 227 | return VMNil, VMErrorNotConverted 228 | } 229 | 230 | func (v *VMMetaObj) MarshalBinary() ([]byte, error) { 231 | var buf bytes.Buffer 232 | enc := gob.NewEncoder(&buf) 233 | if err := enc.Encode(v.vmOriginal); err != nil { 234 | return nil, err 235 | } 236 | return buf.Bytes(), nil 237 | } 238 | 239 | func (v *VMMetaObj) UnmarshalBinary(data []byte) error { 240 | r := bytes.NewReader(data) 241 | dec := gob.NewDecoder(r) 242 | var obj VMMetaObject 243 | if err := dec.Decode(obj); err != nil { 244 | return err 245 | } 246 | obj.VMInit(obj) 247 | obj.VMRegister() 248 | *v = *(obj.(*VMMetaObj)) 249 | return nil 250 | } 251 | 252 | func (v *VMMetaObj) GobEncode() ([]byte, error) { 253 | return v.MarshalBinary() 254 | } 255 | 256 | func (v *VMMetaObj) GobDecode(data []byte) error { 257 | return v.UnmarshalBinary(data) 258 | } 259 | -------------------------------------------------------------------------------- /consulapi/prepared_query.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | // QueryDatacenterOptions sets options about how we fail over if there are no 4 | // healthy nodes in the local datacenter. 5 | type QueryDatacenterOptions struct { 6 | // NearestN is set to the number of remote datacenters to try, based on 7 | // network coordinates. 8 | NearestN int 9 | 10 | // Datacenters is a fixed list of datacenters to try after NearestN. We 11 | // never try a datacenter multiple times, so those are subtracted from 12 | // this list before proceeding. 13 | Datacenters []string 14 | } 15 | 16 | // QueryDNSOptions controls settings when query results are served over DNS. 17 | type QueryDNSOptions struct { 18 | // TTL is the time to live for the served DNS results. 19 | TTL string 20 | } 21 | 22 | // ServiceQuery is used to query for a set of healthy nodes offering a specific 23 | // service. 24 | type ServiceQuery struct { 25 | // Service is the service to query. 26 | Service string 27 | 28 | // Near allows baking in the name of a node to automatically distance- 29 | // sort from. The magic "_agent" value is supported, which sorts near 30 | // the agent which initiated the request by default. 31 | Near string 32 | 33 | // Failover controls what we do if there are no healthy nodes in the 34 | // local datacenter. 35 | Failover QueryDatacenterOptions 36 | 37 | // If OnlyPassing is true then we will only include nodes with passing 38 | // health checks (critical AND warning checks will cause a node to be 39 | // discarded) 40 | OnlyPassing bool 41 | 42 | // Tags are a set of required and/or disallowed tags. If a tag is in 43 | // this list it must be present. If the tag is preceded with "!" then 44 | // it is disallowed. 45 | Tags []string 46 | 47 | // NodeMeta is a map of required node metadata fields. If a key/value 48 | // pair is in this map it must be present on the node in order for the 49 | // service entry to be returned. 50 | NodeMeta map[string]string 51 | } 52 | 53 | // QueryTemplate carries the arguments for creating a templated query. 54 | type QueryTemplate struct { 55 | // Type specifies the type of the query template. Currently only 56 | // "name_prefix_match" is supported. This field is required. 57 | Type string 58 | 59 | // Regexp allows specifying a regex pattern to match against the name 60 | // of the query being executed. 61 | Regexp string 62 | } 63 | 64 | // PrepatedQueryDefinition defines a complete prepared query. 65 | type PreparedQueryDefinition struct { 66 | // ID is this UUID-based ID for the query, always generated by Consul. 67 | ID string 68 | 69 | // Name is an optional friendly name for the query supplied by the 70 | // user. NOTE - if this feature is used then it will reduce the security 71 | // of any read ACL associated with this query/service since this name 72 | // can be used to locate nodes with supplying any ACL. 73 | Name string 74 | 75 | // Session is an optional session to tie this query's lifetime to. If 76 | // this is omitted then the query will not expire. 77 | Session string 78 | 79 | // Token is the ACL token used when the query was created, and it is 80 | // used when a query is subsequently executed. This token, or a token 81 | // with management privileges, must be used to change the query later. 82 | Token string 83 | 84 | // Service defines a service query (leaving things open for other types 85 | // later). 86 | Service ServiceQuery 87 | 88 | // DNS has options that control how the results of this query are 89 | // served over DNS. 90 | DNS QueryDNSOptions 91 | 92 | // Template is used to pass through the arguments for creating a 93 | // prepared query with an attached template. If a template is given, 94 | // interpolations are possible in other struct fields. 95 | Template QueryTemplate 96 | } 97 | 98 | // PreparedQueryExecuteResponse has the results of executing a query. 99 | type PreparedQueryExecuteResponse struct { 100 | // Service is the service that was queried. 101 | Service string 102 | 103 | // Nodes has the nodes that were output by the query. 104 | Nodes []ServiceEntry 105 | 106 | // DNS has the options for serving these results over DNS. 107 | DNS QueryDNSOptions 108 | 109 | // Datacenter is the datacenter that these results came from. 110 | Datacenter string 111 | 112 | // Failovers is a count of how many times we had to query a remote 113 | // datacenter. 114 | Failovers int 115 | } 116 | 117 | // PreparedQuery can be used to query the prepared query endpoints. 118 | type PreparedQuery struct { 119 | c *Client 120 | } 121 | 122 | // PreparedQuery returns a handle to the prepared query endpoints. 123 | func (c *Client) PreparedQuery() *PreparedQuery { 124 | return &PreparedQuery{c} 125 | } 126 | 127 | // Create makes a new prepared query. The ID of the new query is returned. 128 | func (c *PreparedQuery) Create(query *PreparedQueryDefinition, q *WriteOptions) (string, *WriteMeta, error) { 129 | r := c.c.newRequest("POST", "/v1/query") 130 | r.setWriteOptions(q) 131 | r.obj = query 132 | rtt, resp, err := requireOK(c.c.doRequest(r)) 133 | if err != nil { 134 | return "", nil, err 135 | } 136 | defer resp.Body.Close() 137 | 138 | wm := &WriteMeta{} 139 | wm.RequestTime = rtt 140 | 141 | var out struct{ ID string } 142 | if err := decodeBody(resp, &out); err != nil { 143 | return "", nil, err 144 | } 145 | return out.ID, wm, nil 146 | } 147 | 148 | // Update makes updates to an existing prepared query. 149 | func (c *PreparedQuery) Update(query *PreparedQueryDefinition, q *WriteOptions) (*WriteMeta, error) { 150 | return c.c.write("/v1/query/"+query.ID, query, nil, q) 151 | } 152 | 153 | // List is used to fetch all the prepared queries (always requires a management 154 | // token). 155 | func (c *PreparedQuery) List(q *QueryOptions) ([]*PreparedQueryDefinition, *QueryMeta, error) { 156 | var out []*PreparedQueryDefinition 157 | qm, err := c.c.query("/v1/query", &out, q) 158 | if err != nil { 159 | return nil, nil, err 160 | } 161 | return out, qm, nil 162 | } 163 | 164 | // Get is used to fetch a specific prepared query. 165 | func (c *PreparedQuery) Get(queryID string, q *QueryOptions) ([]*PreparedQueryDefinition, *QueryMeta, error) { 166 | var out []*PreparedQueryDefinition 167 | qm, err := c.c.query("/v1/query/"+queryID, &out, q) 168 | if err != nil { 169 | return nil, nil, err 170 | } 171 | return out, qm, nil 172 | } 173 | 174 | // Delete is used to delete a specific prepared query. 175 | func (c *PreparedQuery) Delete(queryID string, q *WriteOptions) (*WriteMeta, error) { 176 | r := c.c.newRequest("DELETE", "/v1/query/"+queryID) 177 | r.setWriteOptions(q) 178 | rtt, resp, err := requireOK(c.c.doRequest(r)) 179 | if err != nil { 180 | return nil, err 181 | } 182 | defer resp.Body.Close() 183 | 184 | wm := &WriteMeta{} 185 | wm.RequestTime = rtt 186 | return wm, nil 187 | } 188 | 189 | // Execute is used to execute a specific prepared query. You can execute using 190 | // a query ID or name. 191 | func (c *PreparedQuery) Execute(queryIDOrName string, q *QueryOptions) (*PreparedQueryExecuteResponse, *QueryMeta, error) { 192 | var out *PreparedQueryExecuteResponse 193 | qm, err := c.c.query("/v1/query/"+queryIDOrName+"/execute", &out, q) 194 | if err != nil { 195 | return nil, nil, err 196 | } 197 | return out, qm, nil 198 | } 199 | -------------------------------------------------------------------------------- /consulapi/operator_autopilot.go: -------------------------------------------------------------------------------- 1 | package consulapi 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "strconv" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | // AutopilotConfiguration is used for querying/setting the Autopilot configuration. 13 | // Autopilot helps manage operator tasks related to Consul servers like removing 14 | // failed servers from the Raft quorum. 15 | type AutopilotConfiguration struct { 16 | // CleanupDeadServers controls whether to remove dead servers from the Raft 17 | // peer list when a new server joins 18 | CleanupDeadServers bool 19 | 20 | // LastContactThreshold is the limit on the amount of time a server can go 21 | // without leader contact before being considered unhealthy. 22 | LastContactThreshold *ReadableDuration 23 | 24 | // MaxTrailingLogs is the amount of entries in the Raft Log that a server can 25 | // be behind before being considered unhealthy. 26 | MaxTrailingLogs uint64 27 | 28 | // ServerStabilizationTime is the minimum amount of time a server must be 29 | // in a stable, healthy state before it can be added to the cluster. Only 30 | // applicable with Raft protocol version 3 or higher. 31 | ServerStabilizationTime *ReadableDuration 32 | 33 | // (Enterprise-only) RedundancyZoneTag is the node tag to use for separating 34 | // servers into zones for redundancy. If left blank, this feature will be disabled. 35 | RedundancyZoneTag string 36 | 37 | // (Enterprise-only) DisableUpgradeMigration will disable Autopilot's upgrade migration 38 | // strategy of waiting until enough newer-versioned servers have been added to the 39 | // cluster before promoting them to voters. 40 | DisableUpgradeMigration bool 41 | 42 | // (Enterprise-only) UpgradeVersionTag is the node tag to use for version info when 43 | // performing upgrade migrations. If left blank, the Consul version will be used. 44 | UpgradeVersionTag string 45 | 46 | // CreateIndex holds the index corresponding the creation of this configuration. 47 | // This is a read-only field. 48 | CreateIndex uint64 49 | 50 | // ModifyIndex will be set to the index of the last update when retrieving the 51 | // Autopilot configuration. Resubmitting a configuration with 52 | // AutopilotCASConfiguration will perform a check-and-set operation which ensures 53 | // there hasn't been a subsequent update since the configuration was retrieved. 54 | ModifyIndex uint64 55 | } 56 | 57 | // ServerHealth is the health (from the leader's point of view) of a server. 58 | type ServerHealth struct { 59 | // ID is the raft ID of the server. 60 | ID string 61 | 62 | // Name is the node name of the server. 63 | Name string 64 | 65 | // Address is the address of the server. 66 | Address string 67 | 68 | // The status of the SerfHealth check for the server. 69 | SerfStatus string 70 | 71 | // Version is the Consul version of the server. 72 | Version string 73 | 74 | // Leader is whether this server is currently the leader. 75 | Leader bool 76 | 77 | // LastContact is the time since this node's last contact with the leader. 78 | LastContact *ReadableDuration 79 | 80 | // LastTerm is the highest leader term this server has a record of in its Raft log. 81 | LastTerm uint64 82 | 83 | // LastIndex is the last log index this server has a record of in its Raft log. 84 | LastIndex uint64 85 | 86 | // Healthy is whether or not the server is healthy according to the current 87 | // Autopilot config. 88 | Healthy bool 89 | 90 | // Voter is whether this is a voting server. 91 | Voter bool 92 | 93 | // StableSince is the last time this server's Healthy value changed. 94 | StableSince time.Time 95 | } 96 | 97 | // OperatorHealthReply is a representation of the overall health of the cluster 98 | type OperatorHealthReply struct { 99 | // Healthy is true if all the servers in the cluster are healthy. 100 | Healthy bool 101 | 102 | // FailureTolerance is the number of healthy servers that could be lost without 103 | // an outage occurring. 104 | FailureTolerance int 105 | 106 | // Servers holds the health of each server. 107 | Servers []ServerHealth 108 | } 109 | 110 | // ReadableDuration is a duration type that is serialized to JSON in human readable format. 111 | type ReadableDuration time.Duration 112 | 113 | func NewReadableDuration(dur time.Duration) *ReadableDuration { 114 | d := ReadableDuration(dur) 115 | return &d 116 | } 117 | 118 | func (d *ReadableDuration) String() string { 119 | return d.Duration().String() 120 | } 121 | 122 | func (d *ReadableDuration) Duration() time.Duration { 123 | if d == nil { 124 | return time.Duration(0) 125 | } 126 | return time.Duration(*d) 127 | } 128 | 129 | func (d *ReadableDuration) MarshalJSON() ([]byte, error) { 130 | return []byte(fmt.Sprintf(`"%s"`, d.Duration().String())), nil 131 | } 132 | 133 | func (d *ReadableDuration) UnmarshalJSON(raw []byte) error { 134 | if d == nil { 135 | return fmt.Errorf("cannot unmarshal to nil pointer") 136 | } 137 | 138 | str := string(raw) 139 | if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' { 140 | return fmt.Errorf("must be enclosed with quotes: %s", str) 141 | } 142 | dur, err := time.ParseDuration(str[1 : len(str)-1]) 143 | if err != nil { 144 | return err 145 | } 146 | *d = ReadableDuration(dur) 147 | return nil 148 | } 149 | 150 | // AutopilotGetConfiguration is used to query the current Autopilot configuration. 151 | func (op *Operator) AutopilotGetConfiguration(q *QueryOptions) (*AutopilotConfiguration, error) { 152 | r := op.c.newRequest("GET", "/v1/operator/autopilot/configuration") 153 | r.setQueryOptions(q) 154 | _, resp, err := requireOK(op.c.doRequest(r)) 155 | if err != nil { 156 | return nil, err 157 | } 158 | defer resp.Body.Close() 159 | 160 | var out AutopilotConfiguration 161 | if err := decodeBody(resp, &out); err != nil { 162 | return nil, err 163 | } 164 | 165 | return &out, nil 166 | } 167 | 168 | // AutopilotSetConfiguration is used to set the current Autopilot configuration. 169 | func (op *Operator) AutopilotSetConfiguration(conf *AutopilotConfiguration, q *WriteOptions) error { 170 | r := op.c.newRequest("PUT", "/v1/operator/autopilot/configuration") 171 | r.setWriteOptions(q) 172 | r.obj = conf 173 | _, resp, err := requireOK(op.c.doRequest(r)) 174 | if err != nil { 175 | return err 176 | } 177 | resp.Body.Close() 178 | return nil 179 | } 180 | 181 | // AutopilotCASConfiguration is used to perform a Check-And-Set update on the 182 | // Autopilot configuration. The ModifyIndex value will be respected. Returns 183 | // true on success or false on failures. 184 | func (op *Operator) AutopilotCASConfiguration(conf *AutopilotConfiguration, q *WriteOptions) (bool, error) { 185 | r := op.c.newRequest("PUT", "/v1/operator/autopilot/configuration") 186 | r.setWriteOptions(q) 187 | r.params.Set("cas", strconv.FormatUint(conf.ModifyIndex, 10)) 188 | r.obj = conf 189 | _, resp, err := requireOK(op.c.doRequest(r)) 190 | if err != nil { 191 | return false, err 192 | } 193 | defer resp.Body.Close() 194 | 195 | var buf bytes.Buffer 196 | if _, err := io.Copy(&buf, resp.Body); err != nil { 197 | return false, fmt.Errorf("Failed to read response: %v", err) 198 | } 199 | res := strings.Contains(string(buf.Bytes()), "true") 200 | 201 | return res, nil 202 | } 203 | 204 | // AutopilotServerHealth 205 | func (op *Operator) AutopilotServerHealth(q *QueryOptions) (*OperatorHealthReply, error) { 206 | r := op.c.newRequest("GET", "/v1/operator/autopilot/health") 207 | r.setQueryOptions(q) 208 | _, resp, err := requireOK(op.c.doRequest(r)) 209 | if err != nil { 210 | return nil, err 211 | } 212 | defer resp.Body.Close() 213 | 214 | var out OperatorHealthReply 215 | if err := decodeBody(resp, &out); err != nil { 216 | return nil, err 217 | } 218 | return &out, nil 219 | } 220 | -------------------------------------------------------------------------------- /services/gonecsvc/jsAceLang.go: -------------------------------------------------------------------------------- 1 | package gonecsvc 2 | 3 | const jsAceLang=`ace.define("ace/mode/gonec_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) { 4 | "use strict"; 5 | 6 | var oop = require("../lib/oop"); 7 | var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; 8 | 9 | var GonecHighlightRules = function() { 10 | 11 | var keywords = ( 12 | "Прервать|Цикл|Новый|Иначе|ИначеЕсли|Для|Функция|Если|из|по|Выбор|Когда|Другое|Старт|Параллельно|"+ 13 | "Возврат|Тогда|каждого|Пока|ИЛИ|И|НЕ|Попытка|ВызватьИсключение|Исключение|Продолжить|Канал|Модуль|"+ 14 | "КонецЦикла|КонецЕсли|КонецФункции|КонецПопытки|КонецВыбора" 15 | ); 16 | 17 | var builtinConstants = ("Истина|Ложь|Неопределено|NULL|ДлительностьНаносекунды|"+ 18 | "ДлительностьМикросекунды|ДлительностьМиллисекунды|ДлительностьСекунды|"+ 19 | "ДлительностьМинуты|ДлительностьЧаса|ДлительностьДня|АргументыЗапуска"); 20 | 21 | var functions = ( 22 | "Число|Строка|Булево|ЦелоеЧисло|Массив|Структура|Дата|Длительность|"+ 23 | "Импорт|Длина|Диапазон|ТекущаяДата|ПрошлоВремениС|Пауза|Хэш|"+ 24 | "УникальныйИдентификатор|ПолучитьМассивИзПула|ВернутьМассивВПул|СлучайнаяСтрока|НРег|ВРег|"+ 25 | "Формат|КодСимвола|ТипЗнч|Сообщить|СообщитьФ|ОбработатьГорутины|ЗагрузитьИВыполнить|"+ 26 | "ОписаниеОшибки|ПеременнаяОкружения|СтрСодержит|СтрСодержитЛюбой|СтрКоличество|СтрНайти|"+ 27 | "СтрНайтиЛюбой|СтрНайтиПоследний|СтрЗаменить|Окр" 28 | ); 29 | 30 | var builtinTypes = ("ГруппаОжидания|Сервер|Клиент|ФайловаяБазаДанных"); 31 | 32 | var keywordMapper = this.createKeywordMapper({ 33 | "keyword": keywords, 34 | "support.function": functions, 35 | "support.type": builtinTypes, 36 | "constant.language": builtinConstants, 37 | "variable.language": "self" 38 | }, "identifier"); 39 | 40 | this.$rules = { 41 | "start" : [ 42 | { 43 | token : "comment", 44 | regex : "\\/\\/.*$" 45 | }, 46 | { 47 | token : "comment", 48 | regex : "\\#.*$" 49 | }, 50 | { 51 | token : "string", // single line 52 | regex : /"(?:[^"\\]|\\.)*?"/ 53 | }, { 54 | token : "string", // raw 55 | regex : '`+"`"+`', 56 | next : "bqstring" 57 | }, { 58 | token : "constant.numeric", // hex 59 | regex : "0[xX][0-9a-fA-F]+\\b" 60 | }, { 61 | token : "constant.numeric", // float 62 | regex : "[+-]?\\d+(?:(?:\\.\\d*)?(?:[eE][+-]?\\d+)?)?\\b" 63 | }, { 64 | token : ["keyword", "text", "entity.name.function"], 65 | regex : "(Функция)(\\s+)([a-zA-Zа-яА-ЯёЁ_$][a-zA-Zа-яА-Я0-9_$]*)(?![a-zA-Zа-яА-ЯёЁ])" 66 | }, { 67 | token : keywordMapper, 68 | regex : "[a-zA-Zа-яА-Я_$][a-zA-Zа-яА-Я0-9_$]*(?![a-zA-Zа-яА-ЯёЁ])" 69 | }, { 70 | token : "keyword.operator", 71 | regex : "!|\\$|%|&|\\*|\\-\\-|\\-|\\+\\+|\\+|~|==|=|!=|<=|>=|<<=|>>=|>>>=|<>|<|>|!|&&|\\|\\||\\:|\\*=|%=|\\+=|\\-=|&=|\\^=" 72 | }, { 73 | token : "paren.lparen", 74 | regex : "[\\[\\(\\{]" 75 | }, { 76 | token : "paren.rparen", 77 | regex : "[\\]\\)\\}]" 78 | }, { 79 | token : "text", 80 | regex : "\\s+|\\w+" 81 | } ], 82 | "bqstring" : [ 83 | { 84 | token : "string", 85 | regex : '`+"`"+`', 86 | next : "start" 87 | }, { 88 | defaultToken : "string" 89 | } 90 | ] 91 | }; 92 | 93 | this.normalizeRules(); 94 | } 95 | 96 | oop.inherits(GonecHighlightRules, TextHighlightRules); 97 | 98 | exports.GonecHighlightRules = GonecHighlightRules; 99 | }); 100 | 101 | ace.define("ace/mode/gonec",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/gonec_highlight_rules","ace/mode/folding/gonec","ace/range","ace/worker/worker_client"], function(require, exports, module) { 102 | "use strict"; 103 | 104 | var oop = require("../lib/oop"); 105 | var TextMode = require("./text").Mode; 106 | var GonecHighlightRules = require("./gonec_highlight_rules").GonecHighlightRules; 107 | var Range = require("../range").Range; 108 | var WorkerClient = require("../worker/worker_client").WorkerClient; 109 | 110 | var Mode = function() { 111 | this.HighlightRules = GonecHighlightRules; 112 | 113 | this.$behaviour = this.$defaultBehaviour; 114 | }; 115 | oop.inherits(Mode, TextMode); 116 | 117 | (function() { 118 | 119 | this.lineCommentStart = "//"; 120 | 121 | var indentKeywords = { 122 | "Функция": 1, 123 | "Тогда": 1, 124 | "Цикл": 1, 125 | "Иначе": 1, 126 | "ИначеЕсли": 1, 127 | "Когда": 1, 128 | "Другое": 1, 129 | "Пока": 1, 130 | "Попытка": 1, 131 | "Исключение": 1, 132 | "КонецЦикла": -1, 133 | "КонецЕсли": -1, 134 | "КонецФункции": -1, 135 | "КонецПопытки": -1, 136 | "КонецВыбора": -1 137 | }; 138 | var outdentKeywords = [ 139 | "Иначе", 140 | "ИначеЕсли", 141 | "КонецЦикла", 142 | "КонецЕсли", 143 | "КонецФункции", 144 | "КонецПопытки", 145 | "КонецВыбора" 146 | ]; 147 | 148 | function getNetIndentLevel(tokens) { 149 | var level = 0; 150 | for (var i = 0; i < tokens.length; i++) { 151 | var token = tokens[i]; 152 | if (token.type == "keyword") { 153 | if (token.value in indentKeywords) { 154 | level += indentKeywords[token.value]; 155 | } 156 | } else if (token.type == "paren.lparen") { 157 | level += token.value.length; 158 | } else if (token.type == "paren.rparen") { 159 | level -= token.value.length; 160 | } 161 | } 162 | if (level < 0) { 163 | return -1; 164 | } else if (level > 0) { 165 | return 1; 166 | } else { 167 | return 0; 168 | } 169 | } 170 | 171 | this.getNextLineIndent = function(state, line, tab) { 172 | var indent = this.$getIndent(line); 173 | var level = 0; 174 | 175 | var tokenizedLine = this.getTokenizer().getLineTokens(line, state); 176 | var tokens = tokenizedLine.tokens; 177 | 178 | if (state == "start") { 179 | level = getNetIndentLevel(tokens); 180 | } 181 | if (level > 0) { 182 | return indent + tab; 183 | } else if (level < 0 && indent.substr(indent.length - tab.length) == tab) { 184 | if (!this.checkOutdent(state, line, "\n")) { 185 | return indent.substr(0, indent.length - tab.length); 186 | } 187 | } 188 | return indent; 189 | }; 190 | 191 | this.checkOutdent = function(state, line, input) { 192 | if (input != "\n" && input != "\r" && input != "\r\n") 193 | return false; 194 | 195 | if (line.match(/^\s*[\)\}\]]$/)) 196 | return true; 197 | 198 | var tokens = this.getTokenizer().getLineTokens(line.trim(), state).tokens; 199 | 200 | if (!tokens || !tokens.length) 201 | return false; 202 | 203 | return (tokens[0].type == "keyword" && outdentKeywords.indexOf(tokens[0].value) != -1); 204 | }; 205 | 206 | this.autoOutdent = function(state, session, row) { 207 | var prevLine = session.getLine(row - 1); 208 | var prevIndent = this.$getIndent(prevLine).length; 209 | var prevTokens = this.getTokenizer().getLineTokens(prevLine, "start").tokens; 210 | var tabLength = session.getTabString().length; 211 | var expectedIndent = prevIndent + tabLength * getNetIndentLevel(prevTokens); 212 | var curIndent = this.$getIndent(session.getLine(row)).length; 213 | if (curIndent <= expectedIndent) { 214 | return; 215 | } 216 | session.outdentRows(new Range(row, 0, row + 2, 0)); 217 | }; 218 | 219 | this.createWorker = function(session) { 220 | var worker = new WorkerClient(["ace"], "ace/mode/gonec_worker", "Worker"); 221 | worker.attachToDocument(session.getDocument()); 222 | 223 | worker.on("annotate", function(e) { 224 | session.setAnnotations(e.data); 225 | }); 226 | 227 | worker.on("terminate", function() { 228 | session.clearAnnotations(); 229 | }); 230 | 231 | return worker; 232 | }; 233 | 234 | this.$id = "ace/mode/gonec"; 235 | }).call(Mode.prototype); 236 | 237 | exports.Mode = Mode; 238 | }); 239 | ` -------------------------------------------------------------------------------- /example/todo.gnc: -------------------------------------------------------------------------------- 1 | #!gonec 2 | // Демонстрационный пример - todo список задач на базе vue.js и bootstrap 3 | 4 | база = Новый ФайловаяБазаДанных 5 | база.Открыть("todo.db") 6 | 7 | // инициируем таблицу, если ее еще нет 8 | тр = база.НачатьТранзакцию(Истина) 9 | тр.Таблица("Задачи") 10 | тр.ЗафиксироватьТранзакцию() 11 | 12 | Функция ГлавнаяСтраница(вых,вх) 13 | вых.Отправить({"Статус":200, "Тело": ` 14 |
15 | 16 | 17 |